@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.
- package/.env.example +0 -15
- package/README.md +5 -5
- package/dist/config.js +0 -37
- package/dist/index.js +0 -2
- package/dist/knowledge/backendConstraints.js +1 -18
- package/dist/knowledge/boxSections.js +184 -0
- package/dist/knowledge/componentCatalog.js +232 -562
- package/dist/knowledge/componentEvents.js +21 -141
- package/dist/knowledge/layoutSummary.js +2 -140
- package/dist/knowledge/normalize.js +67 -236
- package/dist/knowledge/outlines.js +0 -36
- package/dist/knowledge/policy.js +4 -18
- package/dist/knowledge/reservedTables.js +0 -79
- package/dist/knowledge/rules.js +21 -319
- package/dist/knowledge/schemaGuard.js +0 -11
- package/dist/knowledge/styleResolve.js +38 -55
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +3 -6
- package/dist/platform/diagnostics.js +4 -10
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +10 -162
- package/dist/platform/http.js +8 -29
- package/dist/platform/responses.js +5 -39
- package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
- package/dist/platform/secrets.js +1 -17
- package/dist/platform/strictArguments.js +12 -68
- package/dist/platform/tokenVault.js +4 -187
- package/dist/resources/examples/layout-app-shell.json +707 -229
- package/dist/resources/examples/page-detail-profile.json +1 -2
- package/dist/resources/examples/page-dynamic-form.json +2 -3
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +2 -8
- package/dist/resources/guides/component.md +4 -91
- package/dist/resources/guides/entity.md +8 -50
- package/dist/resources/guides/expressions.md +5 -14
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +1 -38
- package/dist/resources/guides/layout.md +58 -55
- package/dist/resources/guides/microservice.md +2 -81
- package/dist/resources/guides/middleware.md +2 -44
- package/dist/resources/guides/page.md +75 -454
- package/dist/resources/guides/theme.md +2 -8
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +1 -58
- package/dist/resources/rules.js +5 -11
- package/dist/resources/schema-fingerprint.json +5 -7
- package/dist/resources/schemas/CreateEntityCommand.json +0 -118
- package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/CreateQueryCommand.json +0 -4
- package/dist/resources/schemas/CreateWorkflowCommand.json +1 -143
- package/dist/resources/schemas/DataJobDto.json +1 -6
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
- package/dist/resources/schemas/MicroserviceDto.json +3 -15
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
- package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -22
- package/dist/resources/schemas/StepDto.json +1 -22
- package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
- package/dist/resources/schemas/UpdateViewCommand.json +2 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -143
- package/dist/server.js +1 -27
- package/dist/tools/access.js +2 -369
- package/dist/tools/applications.js +3 -60
- package/dist/tools/common.js +0 -7
- package/dist/tools/components.js +14 -118
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +2 -2
- package/dist/tools/designSchemaPatch.js +18 -34
- package/dist/tools/dtos.js +5 -36
- package/dist/tools/entities.js +12 -79
- package/dist/tools/externalComponents.js +5 -26
- package/dist/tools/flows.js +14 -122
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +2 -5
- package/dist/tools/layouts.js +17 -50
- package/dist/tools/merges.js +1 -1
- package/dist/tools/microservices.js +25 -77
- package/dist/tools/pages.js +112 -465
- package/dist/tools/platformInfo.js +8 -27
- package/dist/tools/projects.js +9 -71
- package/dist/tools/rows.js +13 -69
- package/dist/tools/schemaOps.js +1 -61
- package/dist/tools/screenWrite.js +8 -37
- package/dist/tools/screens.js +5 -95
- package/dist/tools/templates.js +1 -26
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +5 -11
- package/dist/tools/workflows.js +5 -10
- package/package.json +2 -2
- package/dist/knowledge/access.js +0 -802
- package/dist/knowledge/case.js +0 -41
- package/dist/knowledge/connectDataBind.js +0 -819
- package/dist/knowledge/connectDataDefinition.js +0 -341
- package/dist/knowledge/connectDataExecute.js +0 -93
- package/dist/knowledge/connectDataRules.js +0 -128
- package/dist/knowledge/decision.js +0 -121
- package/dist/knowledge/expressionPrelude.js +0 -59
- package/dist/knowledge/microserviceParameters.js +0 -58
- package/dist/knowledge/mining.js +0 -94
- package/dist/knowledge/mobileComponentCatalog.js +0 -859
- package/dist/knowledge/mobileComponentEvents.js +0 -239
- package/dist/knowledge/platform/lint.js +0 -385
- package/dist/knowledge/platform/mobile.js +0 -142
- package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
- package/dist/knowledge/platform/mobileRules.js +0 -231
- package/dist/knowledge/platform/mobileSpacing.js +0 -82
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/nativeBoxModel.js +0 -44
- package/dist/knowledge/platform/profile.js +0 -25
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -39
- package/dist/knowledge/process.js +0 -510
- package/dist/knowledge/rowFilter.js +0 -43
- package/dist/knowledge/styleResolveNative.js +0 -527
- package/dist/platform/argumentAliases.js +0 -128
- package/dist/platform/bodyFile.js +0 -103
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/console-log.js +0 -122
- package/dist/platform/capture/index.js +0 -11
- package/dist/platform/capture/readiness.js +0 -103
- package/dist/platform/capture/summary.js +0 -77
- package/dist/platform/capture/target.js +0 -5
- package/dist/platform/capture/targets/studio-mobile.js +0 -15
- package/dist/platform/capture/targets/studio-web.js +0 -5
- package/dist/platform/impersonation.js +0 -45
- package/dist/platform/jsonArray.js +0 -24
- package/dist/platform/schemaReadMemory.js +0 -60
- package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
- package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
- package/dist/resources/examples/bpm-satinalma.json +0 -376
- package/dist/resources/examples/connect-data-convert-verify.json +0 -107
- package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
- package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
- package/dist/resources/examples/connect-data-query-params.json +0 -50
- package/dist/resources/examples/connect-data-table-entity.json +0 -147
- package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
- package/dist/resources/examples/mobile-form-screen.json +0 -365
- package/dist/resources/examples/mobile-list-detail-form.json +0 -672
- package/dist/resources/guides/access.md +0 -796
- package/dist/resources/guides/bpm.md +0 -686
- package/dist/resources/guides/case.md +0 -153
- package/dist/resources/guides/decision.md +0 -185
- package/dist/resources/guides/mobile-layout.md +0 -193
- package/dist/resources/guides/mobile.md +0 -495
- package/dist/resources/read.js +0 -41
- package/dist/resources/schemas/AccessExplanationDto.json +0 -144
- package/dist/resources/schemas/AccessMatrixDto.json +0 -170
- package/dist/resources/schemas/AccessPermissionDto.json +0 -57
- package/dist/resources/schemas/AccessPolicyDto.json +0 -223
- package/dist/resources/schemas/AccessResourceDto.json +0 -86
- package/dist/resources/schemas/AppSessionDto.json +0 -53
- package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
- package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
- package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
- package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
- package/dist/resources/schemas/DecisionDto.json +0 -302
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
- package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
- package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
- package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
- package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
- package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
- package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
- package/dist/tools/accessOperations.js +0 -135
- package/dist/tools/accessPolicies.js +0 -170
- package/dist/tools/accessRuntime.js +0 -203
- package/dist/tools/bpm.js +0 -343
- package/dist/tools/bpmGovernance.js +0 -266
- package/dist/tools/bpmOperations.js +0 -230
- package/dist/tools/bpmReporting.js +0 -154
- package/dist/tools/bpmRouting.js +0 -150
- package/dist/tools/bpmRuntime.js +0 -396
- package/dist/tools/case.js +0 -178
- package/dist/tools/caseRuntime.js +0 -89
- package/dist/tools/connectData.js +0 -449
- package/dist/tools/connectors.js +0 -193
- package/dist/tools/decisions.js +0 -206
- package/dist/tools/mobile.js +0 -221
- package/dist/tools/modules.js +0 -48
- package/dist/tools/organizationStructure.js +0 -137
- package/dist/tools/profileWrite.js +0 -92
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, readOnly, writes, updates } from './common.js';
|
|
2
|
-
import { accessResourceTypes } from '../knowledge/access.js';
|
|
3
|
-
import { PlatformApiError } from '../platform/errors.js';
|
|
4
|
-
import { handle } from '../platform/responses.js';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
const enforcementModes = ['off', 'shadow', 'enforce'];
|
|
7
|
-
const isoDate = z.string().min(1)
|
|
8
|
-
.describe("ISO 8601 zaman damgası (UTC), ör. '2026-09-01T00:00:00Z'");
|
|
9
|
-
function asRecord(value) {
|
|
10
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
11
|
-
}
|
|
12
|
-
export function registerAccessOperationTools(server, platform) {
|
|
13
|
-
const config = platform.config;
|
|
14
|
-
server.registerTool('access_matrix_get', {
|
|
15
|
-
title: 'Politika matrisini getir',
|
|
16
|
-
description: "Bir politikanın izin × kaynak matrisini getirir (GET /{project}/access/policies/{id}/matrix): actions sütunları (izin slug'ı + supportsScope/supportsFieldAccess), rows satırları (kaynak) ve her hücrede o politikadaki grant (grantId · effect · scope · fieldAccess · hasCondition). Politikayı grant grant okumaktan farkı: matris kaynak tipini bir tablo olarak gösterir, boş hücre 'grant yok' demektir. access_matrix_save'in şablonudur.",
|
|
17
|
-
inputSchema: {
|
|
18
|
-
project: projectParam,
|
|
19
|
-
environment: environmentParam,
|
|
20
|
-
id: guid.describe("Politika id'si (access_policy_list)"),
|
|
21
|
-
resourceType: z.enum(accessResourceTypes).describe('Matrisin kaynak tipi — zorunlu; her çağrı tek tipi tablolar'),
|
|
22
|
-
parentId: guid.optional().describe("Katalog satırının id'si (access_resource_list 'id'): o kaynağın çocuklarını tablolar — bir entity'nin alanları için entity katalog satırının id'si")
|
|
23
|
-
},
|
|
24
|
-
annotations: readOnly
|
|
25
|
-
}, handle(config, async ({ project, environment, id, resourceType, parentId }) => {
|
|
26
|
-
const payload = await platform.tenant('GET', project, `/access/policies/${id}/matrix`, { environment, query: { resourceType, ...(parentId ? { parentId } : {}) } });
|
|
27
|
-
const data = asRecord(payload?.data);
|
|
28
|
-
const rows = Array.isArray(data.rows) ? data.rows.map(asRecord) : [];
|
|
29
|
-
return {
|
|
30
|
-
...payload,
|
|
31
|
-
summary: {
|
|
32
|
-
policyId: data.policyId,
|
|
33
|
-
resourceType: data.resourceType,
|
|
34
|
-
actions: Array.isArray(data.actions) ? data.actions.length : 0,
|
|
35
|
-
rows: rows.length,
|
|
36
|
-
filledCells: rows.reduce((total, row) => total + (Array.isArray(row.cells) ? row.cells.map(asRecord).filter(cell => cell.grantId !== undefined && cell.grantId !== null).length : 0), 0)
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}));
|
|
40
|
-
server.registerTool('access_matrix_save', {
|
|
41
|
-
title: 'Politika matrisini kaydet',
|
|
42
|
-
description: "Matris üzerinden grant ekler/günceller/siler (PUT /{project}/access/policies/{id}/matrix). 'changes' verdiğiniz grant'leri yazar (id varsa günceller, yoksa açar), 'removedGrantIds' silinecek grant'lerin id'leridir. Politikanın ATAMALARINA dokunmaz — atama access_policy_update'in işidir. access_policy_update'ten farkı: politikanın grant listesinin tamamını göndermek gerekmez, yalnız değişen hücreler gider. Gövde platformun FluentValidation kısıtlarından geçer (deny grant'i ALAN ERİŞİMİ taşımaz; kapsam ve koşul taşıyabilir — o zaman eylemi kesmez, yalnız kapsamındaki satırları süzer, K-10/TB-94). Politikada SÜRESİ DOLMAMIŞ bir kullanıcı istisnası (`overridesDeny`; henüz başlamamış, ileri tarihli olanı da sayılır) varsa grant'lerine dokunmak — ekleme, silme ya da değiştirme — `access.override.manage` iznini ister; izinsiz istek 403 `AccessOverridePermissionRequiredException` döner ve hiçbir şey yazılmaz (ACC-009).",
|
|
43
|
-
inputSchema: {
|
|
44
|
-
project: projectParam,
|
|
45
|
-
environment: environmentParam,
|
|
46
|
-
id: guid.describe("Politika id'si"),
|
|
47
|
-
resourceType: z.enum(accessResourceTypes).optional().describe('Yanıtta dönecek matrisin kaynak tipi'),
|
|
48
|
-
parentId: guid.optional().describe('Yanıtta dönecek matrisin katalog kökü'),
|
|
49
|
-
changes: z.array(z.record(z.unknown())).optional().describe("Yazılacak grant'ler (access_matrix_get hücre biçimi + permission/resourceKey); boş bırakılabilir"),
|
|
50
|
-
removedGrantIds: z.array(guid).optional().describe("Silinecek grant id'leri")
|
|
51
|
-
},
|
|
52
|
-
annotations: updates
|
|
53
|
-
}, handle(config, ({ project, environment, id, resourceType, parentId, changes, removedGrantIds }) => {
|
|
54
|
-
if ((changes ?? []).length === 0 && (removedGrantIds ?? []).length === 0) {
|
|
55
|
-
throw new PlatformApiError("access_matrix_save: ne 'changes' ne 'removedGrantIds' verildi — istek matriste hiçbir şeyi değiştirmezdi, gönderilmedi.", 400);
|
|
56
|
-
}
|
|
57
|
-
return platform.tenant('PUT', project, `/access/policies/${id}/matrix`, {
|
|
58
|
-
environment,
|
|
59
|
-
body: { changes: changes ?? [], removedGrantIds: removedGrantIds ?? [], ...(resourceType ? { resourceType } : {}), ...(parentId ? { parentId } : {}) }
|
|
60
|
-
});
|
|
61
|
-
}));
|
|
62
|
-
server.registerTool('access_resource_rebuild', {
|
|
63
|
-
title: 'Kaynak kataloğunu yeniden kur',
|
|
64
|
-
description: "Kaynak kataloğunu projenin güncel tanımlarından yeniden üretir (POST /{project}/access/resources/rebuild): yeni entity/alan/uç/sayfa katalogda yoksa ya da silinmiş kaynakların satırları (access_policy_list'te orphaned: true) kaldıysa bunu düzeltir. Grant'ler resourceKey ile bağlıdır, anahtar değişmediği sürece rebuild politikaları bozmaz. Katalog yalnız kompleks projede vardır.",
|
|
65
|
-
inputSchema: { project: projectParam, environment: environmentParam },
|
|
66
|
-
annotations: writes
|
|
67
|
-
}, handle(config, async ({ project, environment }) => {
|
|
68
|
-
const payload = await platform.tenant('POST', project, '/access/resources/rebuild', { environment });
|
|
69
|
-
return { ...payload, note: "Katalog yeniden kuruldu; grant'lerin bağlandığı anahtarları access_resource_list ile doğrulayın." };
|
|
70
|
-
}));
|
|
71
|
-
server.registerTool('access_decision_log_list', {
|
|
72
|
-
title: 'Erişim karar kayıtlarını listele',
|
|
73
|
-
description: "Karar kayıtlarını listeler (GET /{project}/access/decision-logs): zaman penceresi, karar, izin, kaynak anahtarı, neden, satır kapsamının metni, istek yolu ve korelasyon id'si. İki tür yalnız gölge modda yazılır — 'would-deny' (zorlama AÇIK OLSAYDI istek reddedilecekti) ve 'would-filter' (istek geçerdi ama satırları kapsam süzerdi); iki tür ise **enforce modda da** yazılır: 'unrestricted' (isUnrestricted sorgu koştu — TB-103) ve 'elevated' (runAs: system koştu — TB-105). enforce'a geçmeden önce okunacak liste budur; geçildikten sonra da yükseltme izi buradadır. Tek tek kayıtlar için bu, toplamlar için access_decision_log_summary, en çok engellenen kullanıcılar için access_decision_log_users.",
|
|
74
|
-
inputSchema: {
|
|
75
|
-
project: projectParam,
|
|
76
|
-
environment: environmentParam,
|
|
77
|
-
from: isoDate,
|
|
78
|
-
to: isoDate,
|
|
79
|
-
decision: z.enum(['would-deny', 'would-filter', 'would-mask', 'unrestricted', 'elevated']).optional().describe("Süzgeç; VERİLMEZSE tüm kararlar döner — ölçüldü 2026-09-19: aynı pencerede süzgeçsiz 10, 'would-filter' 7, 'would-deny' 3 kayıt. 'would-deny' = zorlama açık olsaydı istek reddedilirdi, 'would-filter' = istek geçerdi ama satırlar kapsamla süzülürdü (okuma yolunda filteredRowCount kaç satırın düşeceğini yazar), 'would-mask' = alan maskeli ya da gizli dönerdi (permission 'field.read', resourceKey 'field:<entityId>:<fieldId>', reason 'masked'|'hidden'), 'unrestricted' = isUnrestricted bir sorgu koştu (resourceKey entity anahtarı, reason entity slug'ı), 'elevated' = runAs: system bir akış koştu (resourceKey 'workflow:<id>', reason akış slug'ı, rowScopeText 'author:<akisi yazan kullanici>'). Son ikisi enforce modda da yazılır. Platform yalnız bu beşini yazar (AccessDecisionLogKind); tanımadığı bir değeri doğrulamaz, sessizce BOŞ liste döndürür"),
|
|
80
|
-
pageIndex: z.number().int().min(0).optional(),
|
|
81
|
-
pageLength: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200')
|
|
82
|
-
},
|
|
83
|
-
annotations: readOnly
|
|
84
|
-
}, handle(config, async ({ project, environment, ...query }) => {
|
|
85
|
-
const payload = await platform.tenant('GET', project, '/access/decision-logs', { environment, query });
|
|
86
|
-
const rows = Array.isArray(payload?.data) ? payload.data.map(asRecord) : [];
|
|
87
|
-
return {
|
|
88
|
-
...payload,
|
|
89
|
-
pageSummary: {
|
|
90
|
-
rows: rows.length,
|
|
91
|
-
withFilteredRowCount: rows.filter(row => typeof row.filteredRowCount === 'number').length,
|
|
92
|
-
wouldMask: rows.filter(row => row.decision === 'would-mask').length
|
|
93
|
-
},
|
|
94
|
-
note: "Sayımlar YALNIZ dönen sayfayı kapsar (pageSummary), pencerenin tamamını değil — pencere toplamı için access_decision_log_summary. filteredRowCount yalnız OKUMA yolundaki 'would-filter' kayıtlarında doludur (toplam − kapsamlı toplam) ve satırın PENCERE TOPLAMIDIR, istek başına değil: istek başına düşen satır sayısı için filteredRowCount / count (ölçüldü 2026-09-19: count 22, filteredRowCount 352 → istek başına 16). Güncelleme ve silme yolunda null kalır. 'would-mask' istek başına entity başına bir kez ÇÖZÜLÜR, ama maskeli/gizli HER ALAN için bir kayıt düşer ve yalnız sorgu satır döndürdüğünde yazılır."
|
|
95
|
-
};
|
|
96
|
-
}));
|
|
97
|
-
server.registerTool('access_decision_log_summary', {
|
|
98
|
-
title: 'Karar kayıtlarını özetle',
|
|
99
|
-
description: "Karar kayıtlarını izin × kaynak × neden kırılımında toplar (GET /{project}/access/decision-logs/summary). Gölge modda 'hangi izin nerede eksik' sorusunun cevabı budur: en çok tekrar eden satır, politikanın en belirgin boşluğudur. Tek tek kayıtlar için access_decision_log_list.",
|
|
100
|
-
inputSchema: { project: projectParam, environment: environmentParam, from: isoDate, to: isoDate },
|
|
101
|
-
annotations: readOnly
|
|
102
|
-
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/decision-logs/summary', { environment, query })));
|
|
103
|
-
server.registerTool('access_decision_log_users', {
|
|
104
|
-
title: 'Engellenen kullanıcıları listele',
|
|
105
|
-
description: "Karar kayıtlarını kullanıcı kırılımında toplar (GET /{project}/access/decision-logs/users): en çok 'reddedilirdi' kaydı üreten kullanıcılar. Bir kullanıcı listenin başındaysa çoğu zaman pozisyonu ya da atanan politikası eksiktir — access_preview ile o kullanıcının tek bir kararını açıklayın.",
|
|
106
|
-
inputSchema: { project: projectParam, environment: environmentParam, from: isoDate, to: isoDate, limit: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200') },
|
|
107
|
-
annotations: readOnly
|
|
108
|
-
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/decision-logs/users', { environment, query })));
|
|
109
|
-
server.registerTool('access_enforcement_readiness', {
|
|
110
|
-
title: 'Zorlamaya hazır mı',
|
|
111
|
-
description: "Son N günün gölge kayıtlarında kaç 'reddedilirdi' olduğunu sayar ve sıfırsa hazır der (POST /{project}/access/enforcement/readiness). enforce'a geçmeden önceki kapı budur: isReady false ise geçiş bugün çalışan bir şeyi kesecek demektir — önce access_decision_log_summary ile boşlukları kapatın. Sayı sıfır olsa bile hiç trafik görmemiş bir uç kanıtlanmış olmaz.",
|
|
112
|
-
inputSchema: { project: projectParam, environment: environmentParam, days: z.number().int().min(1).max(30).optional().describe('Pencere (gün); varsayılan 7, en çok 30') },
|
|
113
|
-
annotations: readOnly
|
|
114
|
-
}, handle(config, async ({ project, environment, days }) => {
|
|
115
|
-
const payload = await platform.tenant('POST', project, '/access/enforcement/readiness', { environment, body: days === undefined ? {} : { days } });
|
|
116
|
-
const data = asRecord(payload?.data);
|
|
117
|
-
return {
|
|
118
|
-
...payload,
|
|
119
|
-
note: data.isReady === true
|
|
120
|
-
? "Pencerede 'reddedilirdi' kaydı yok. Bu, zorlamanın hiçbir şeyi kesmeyeceğini KANITLAMAZ — yalnız o pencerede denenen yolları kapsar; az kullanılan uçları access_preview ile ayrıca sınayın."
|
|
121
|
-
: `Pencerede ${String(data.wouldDenyCount ?? '?')} 'reddedilirdi' kaydı var: zorlama açılırsa bunlar kesilir. access_decision_log_summary ile izin/kaynak kırılımına bakın.`
|
|
122
|
-
};
|
|
123
|
-
}));
|
|
124
|
-
server.registerTool('access_enforcement_mode_set', {
|
|
125
|
-
title: 'Zorlama modunu değiştir',
|
|
126
|
-
description: "Bir environment'ın zorlama modunu değiştirir (PUT /environments/{projectId}/{environmentId}/enforcement-mode): 'off' karar motoru kapalı, 'shadow' kararlar hesaplanır ve reddedilecekler kaydedilir ama istek kesilmez, 'enforce' kararlar uygulanır. Yalnız kompleks projede anlamlıdır. 'enforce'a geçiş ÜRETİM DAVRANIŞINI DEĞİŞTİRİR: önce access_enforcement_readiness'ı yeşile getirin. Platform iki durumda confirm ister: production ortamında HER değişiklik ve HER ortamda 'off'a geçiş (409 EnforcementOffRequiresConfirmation) — 'off' satır kapsamını kaldırmakla kalmaz, korumalı alanları da herkese açık döndürür (TB-99).",
|
|
127
|
-
inputSchema: {
|
|
128
|
-
projectId: guid,
|
|
129
|
-
environmentId: guid.describe('environment_list çıktısındaki id'),
|
|
130
|
-
enforcementMode: z.enum(enforcementModes),
|
|
131
|
-
confirm: z.boolean().optional().describe("Platformun istediği onay: production'da her değişiklik, her ortamda 'off'a geçiş. Bilinçli geçişte true")
|
|
132
|
-
},
|
|
133
|
-
annotations: updates
|
|
134
|
-
}, handle(config, ({ projectId, environmentId, enforcementMode, confirm }) => platform.org('PUT', `/environments/${projectId}/${environmentId}/enforcement-mode`, { body: { enforcementMode, confirm: confirm === true } })));
|
|
135
|
-
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
-
import { accessPolicyEntityFindings, accessResourceTypes, accessScopes } from '../knowledge/access.js';
|
|
3
|
-
import { evaluatePolicy, enforcePolicy, attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
4
|
-
import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
|
|
5
|
-
import { withNormalization } from '../knowledge/normalize.js';
|
|
6
|
-
import { policyRules } from '../knowledge/rules.js';
|
|
7
|
-
import { handle } from '../platform/responses.js';
|
|
8
|
-
import { RefResolver } from '../platform/refs.js';
|
|
9
|
-
import { z } from 'zod';
|
|
10
|
-
const policyOverrideParam = z.boolean().optional()
|
|
11
|
-
.describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
|
|
12
|
-
const grantInput = z.object({
|
|
13
|
-
id: guid.optional().describe('Var olan grant güncelleniyorsa onun id\'si; yeni grant\'te verilmez'),
|
|
14
|
-
permission: z.string().min(1).describe("İzin slug'ı (access_permission_list): 'entity.read', 'field.read', 'microservice.execute' …"),
|
|
15
|
-
resourceKey: z.string().min(1).nullable().optional().describe("Kaynak anahtarı (access_resource_list 'key'): 'entity:<entityId>', 'field:<entityId>:<fieldId>', '<tip>:<id>'; verilmezse iznin tipinin TAMAMI"),
|
|
16
|
-
effect: z.enum(['allow', 'deny']).describe("Kapsamsız ya da koşulsuz scope: 'all' taşıyan 'deny' eylemi keser (TB-132); daraltıcı kapsamlı (own · unit · unit-tree · subordinates · function · company) ya da scope: 'all' + koşullu 'deny' eylemi kesmez, yalnız kapsamındaki / koşula uyan satırları süzer. 'deny' alan erişimi taşımaz (K-10)"),
|
|
17
|
-
scope: z.enum(accessScopes).optional().describe('Satır kapsamı; yalnız entity.read/update/delete/export/reveal/reassign'),
|
|
18
|
-
subordinatesLine: z.enum(['administrative', 'functional', 'both']).optional().describe("Yalnız scope: 'subordinates' ile; varsayılan idari hat"),
|
|
19
|
-
fieldAccess: z.enum(['hidden', 'masked', 'plain']).optional().describe("Yalnız 'field.read' izninde"),
|
|
20
|
-
condition: z.record(z.unknown()).optional().describe("Kapsama AND ile eklenen koşul: { all|any: [...] } ya da { field, op, value }; op eq·neq·in·notin·lt·lte·gt·gte·isnull·isnotnull; value sabit, dizi, { subject: 'userId'|'unitIds'|'positionIds'|'companyIds'|'roleSlugs'|'functionCodes' } ya da { now: true }. Derinlik ≤ 5, düğüm ≤ 50")
|
|
21
|
-
});
|
|
22
|
-
const assignmentInput = z.object({
|
|
23
|
-
id: guid.optional(),
|
|
24
|
-
subjectType: z.enum(['role', 'position', 'unit', 'user', 'authenticated']),
|
|
25
|
-
subjectId: guid.describe("roles / organization_positions / organization_units / users satır id'si (row_list)"),
|
|
26
|
-
includeDescendants: z.boolean().optional().describe("Yalnız subjectType: 'unit' — alt birimleri de kapsar"),
|
|
27
|
-
startsAt: z.string().optional().describe('ISO tarih; verilmezse şimdi'),
|
|
28
|
-
endsAt: z.string().optional().describe('ISO tarih; startsAt\'ten sonra'),
|
|
29
|
-
overridesDeny: z.boolean().optional().describe("Yalnız subjectType: 'user' ve dolu endsAt ile; kapsamlı/kapsamsız bütün deny'leri aşar; yazmak access.override.manage izni ister — 403 alırsanız izni olan hesapla tekrarlayın")
|
|
30
|
-
});
|
|
31
|
-
function compact(value) {
|
|
32
|
-
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
33
|
-
}
|
|
34
|
-
function grantFromRecord(record) {
|
|
35
|
-
return compact({
|
|
36
|
-
id: record.id,
|
|
37
|
-
permission: String(record.permission ?? ''),
|
|
38
|
-
resourceKey: (record.resourceKey ?? undefined),
|
|
39
|
-
effect: record.effect,
|
|
40
|
-
scope: (record.scope ?? undefined),
|
|
41
|
-
subordinatesLine: (record.subordinatesLine ?? undefined),
|
|
42
|
-
fieldAccess: (record.fieldAccess ?? undefined),
|
|
43
|
-
condition: (record.condition ?? undefined)
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
function assignmentFromRecord(record) {
|
|
47
|
-
return compact({
|
|
48
|
-
id: record.id,
|
|
49
|
-
subjectType: record.subjectType,
|
|
50
|
-
subjectId: String(record.subjectId ?? ''),
|
|
51
|
-
includeDescendants: (record.includeDescendants ?? undefined),
|
|
52
|
-
startsAt: (record.startsAt ?? undefined),
|
|
53
|
-
endsAt: (record.endsAt ?? undefined),
|
|
54
|
-
overridesDeny: (record.overridesDeny ?? undefined)
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
async function lintPolicy(platform, project, environment, body, operation, policyOverride, entityBody) {
|
|
58
|
-
const config = platform.config;
|
|
59
|
-
const resolver = new RefResolver(platform, project, environment);
|
|
60
|
-
return [
|
|
61
|
-
...enforcePolicy(evaluatePolicy(config, policyRules, 'accessPolicy', body, { operation }, policyOverride)),
|
|
62
|
-
...enforcePolicy(applyPolicyMode(config, await accessPolicyEntityFindings(platform, resolver, entityBody), policyOverride))
|
|
63
|
-
];
|
|
64
|
-
}
|
|
65
|
-
export function registerAccessPolicyTools(server, platform) {
|
|
66
|
-
const config = platform.config;
|
|
67
|
-
server.registerTool('access_permission_list', {
|
|
68
|
-
title: 'İzin kataloğunu listele',
|
|
69
|
-
description: "Kompleks projenin izin kataloğunu döner (slug · resourceType · action · supportsScope · supportsFieldAccess) — grant'lerin permission değerinin tek kaynağı. Katalog YALNIZ kompleks projede tohumlanır: boş dizi projenin basit olduğunu söyler.",
|
|
70
|
-
inputSchema: { project: projectParam, environment: environmentParam },
|
|
71
|
-
annotations: readOnly
|
|
72
|
-
}, handle(config, async ({ project, environment }) => {
|
|
73
|
-
const payload = await platform.tenant('GET', project, '/access/permissions', { environment });
|
|
74
|
-
const count = Array.isArray(payload?.data) ? payload.data.length : 0;
|
|
75
|
-
return count > 0
|
|
76
|
-
? payload
|
|
77
|
-
: { ...payload, data: [], complex: false, note: "İzin kataloğu boş: proje KOMPLEKS DEĞİL (katalog yalnız accessModel 'complex' projede tohumlanır). project_get accessModel ile doğrulayın; basit projede yetki middleware ve page.roleIds ile kurulur." };
|
|
78
|
-
}));
|
|
79
|
-
server.registerTool('access_resource_list', {
|
|
80
|
-
title: 'Kaynak kataloğunu listele',
|
|
81
|
-
description: "Grant'in bağlanabileceği kaynakları (entity, alan, microservice, sayfa …) sayfalı listeler; 'key' grant'in resourceKey'idir (K-31). Türetilmiş alanlar katalogdadır (isDerived), gizli sistem alanları değildir; isProtected korumalı alanı işaretler. parentId bir KATALOG id'sidir (listedeki 'id'), entity id değil — bir entity'nin alanları için önce entity satırının id'sini alın.",
|
|
82
|
-
inputSchema: {
|
|
83
|
-
project: projectParam,
|
|
84
|
-
environment: environmentParam,
|
|
85
|
-
type: z.enum(accessResourceTypes).optional(),
|
|
86
|
-
parentId: guid.optional().describe("Katalog satırının id'si: o kaynak ve doğrudan çocukları"),
|
|
87
|
-
search: z.string().optional(),
|
|
88
|
-
pageIndex: z.number().int().min(0).optional(),
|
|
89
|
-
pageLength: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200')
|
|
90
|
-
},
|
|
91
|
-
annotations: readOnly
|
|
92
|
-
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/resources', { environment, query })));
|
|
93
|
-
server.registerTool('access_policy_list', {
|
|
94
|
-
title: 'Erişim politikalarını listele',
|
|
95
|
-
description: "Projenin erişim politikalarını grant ve atamalarıyla listeler. origin: system · project · runtime (dikey çözümün production'da ayrıştırdığı, deployment dokunmaz) · module; orphaned: true grant'in kaynağı artık yok.",
|
|
96
|
-
inputSchema: { project: projectParam, environment: environmentParam },
|
|
97
|
-
annotations: readOnly
|
|
98
|
-
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/access/policies', { environment })));
|
|
99
|
-
server.registerTool('access_policy_get', {
|
|
100
|
-
title: 'Erişim politikasını getir',
|
|
101
|
-
description: 'Tek politikayı grant ve atamalarıyla getirir; access_policy_update için şablondur.',
|
|
102
|
-
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
103
|
-
annotations: readOnly
|
|
104
|
-
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/access/policies/${id}`, { environment })));
|
|
105
|
-
server.registerTool('access_policy_create', {
|
|
106
|
-
title: 'Erişim politikası oluştur',
|
|
107
|
-
description: "Kompleks projede politika açar: grant'ler (izin × kaynak × etki × kapsam/koşul/alan erişimi) ve atamalar (rol · pozisyon · birim · kullanıcı) tek gövdede. Gövde istek gitmeden ACC-001 (grant/atama biçimi), ACC-002 (koşulda korumalı alan), ACC-003 (sahiplik alanı olmayan entity'ye kapsam, warn) ve ACC-017 (korumasız alana field.read 'masked' — maske alanın entity_protection_update korumasından gelir, korumasız alanda değer düz döner; warn) kontrolünden geçer. Tasarım eseridir: development'ta (ya da dalda) yazılır, production'a deployment taşır. Kavramlar ve CZA örneği: platform_guide('access').",
|
|
108
|
-
inputSchema: {
|
|
109
|
-
project: projectParam,
|
|
110
|
-
environment: environmentParam,
|
|
111
|
-
name: z.string().min(1).max(150),
|
|
112
|
-
slug: z.string().min(1).max(150).describe('Projede tekil (409 AccessPolicySlugDuplicate)'),
|
|
113
|
-
description: z.string().optional(),
|
|
114
|
-
grants: z.array(grantInput).optional().describe('Verilmezse boş'),
|
|
115
|
-
assignments: z.array(assignmentInput).optional().describe('Verilmezse boş — atamasız politika kimseye uygulanmaz'),
|
|
116
|
-
policyOverride: policyOverrideParam
|
|
117
|
-
},
|
|
118
|
-
annotations: writes
|
|
119
|
-
}, handle(config, async ({ project, environment, name, slug, description, grants, assignments, policyOverride }) => {
|
|
120
|
-
const body = compact({ name, slug, description, grants: (grants ?? []).map(compact), assignments: (assignments ?? []).map(compact) });
|
|
121
|
-
const findings = await lintPolicy(platform, project, environment, body, 'create', policyOverride, body);
|
|
122
|
-
const drift = unmappedMembers('CreateAccessPolicyCommand', body);
|
|
123
|
-
const payload = await platform.tenant('POST', project, '/access/policies', { environment, body });
|
|
124
|
-
return attachFindings(withUnmappedMembers(payload, drift), findings);
|
|
125
|
-
}));
|
|
126
|
-
server.registerTool('access_policy_update', {
|
|
127
|
-
title: 'Erişim politikasını güncelle',
|
|
128
|
-
description: "Politikayı günceller (platform ucu tam değiştirmedir). Gateway kaydı önce okur: VERİLMEYEN alan mevcut değeriyle taşınır ve bu gatewayNormalized ile söylenir. grants / assignments VERİLİRSE TAM LİSTEDİR — listede olmayan grant ya da atama SİLİNİR; var olanı değiştirmek için id'sini koruyun. Yetim anahtarlı var olan grant PUT'u düşürmez, yeni gelen bilinmeyen anahtar 400 AccessResourceUnknown. Aynı ACC-001…003 ve ACC-017 kontrolü (yalnız bu çağrıda verilen grant'lere) uygulanır.",
|
|
129
|
-
inputSchema: {
|
|
130
|
-
project: projectParam,
|
|
131
|
-
environment: environmentParam,
|
|
132
|
-
id: guid,
|
|
133
|
-
name: z.string().min(1).max(150).optional(),
|
|
134
|
-
slug: z.string().min(1).max(150).optional(),
|
|
135
|
-
description: z.string().optional(),
|
|
136
|
-
grants: z.array(grantInput).optional().describe('TAM liste; verilmezse mevcut grant\'ler taşınır'),
|
|
137
|
-
assignments: z.array(assignmentInput).optional().describe('TAM liste; verilmezse mevcut atamalar taşınır'),
|
|
138
|
-
policyOverride: policyOverrideParam
|
|
139
|
-
},
|
|
140
|
-
annotations: updates
|
|
141
|
-
}, handle(config, async ({ project, environment, id, name, slug, description, grants, assignments, policyOverride }) => {
|
|
142
|
-
const current = ((await platform.tenant('GET', project, `/access/policies/${id}`, { environment }))?.data ?? {});
|
|
143
|
-
const carried = [
|
|
144
|
-
...(name === undefined ? ['name'] : []),
|
|
145
|
-
...(slug === undefined ? ['slug'] : []),
|
|
146
|
-
...(description === undefined && typeof current.description === 'string' ? ['description'] : []),
|
|
147
|
-
...(grants === undefined ? ['grants'] : []),
|
|
148
|
-
...(assignments === undefined ? ['assignments'] : [])
|
|
149
|
-
];
|
|
150
|
-
const body = compact({
|
|
151
|
-
name: name ?? String(current.name ?? ''),
|
|
152
|
-
slug: slug ?? String(current.slug ?? ''),
|
|
153
|
-
description: description ?? (typeof current.description === 'string' ? current.description : undefined),
|
|
154
|
-
grants: grants !== undefined ? grants.map(compact) : (Array.isArray(current.grants) ? current.grants : []).map(entry => grantFromRecord(entry)),
|
|
155
|
-
assignments: assignments !== undefined ? assignments.map(compact) : (Array.isArray(current.assignments) ? current.assignments : []).map(entry => assignmentFromRecord(entry))
|
|
156
|
-
});
|
|
157
|
-
// Taşınan grant'ler platformda zaten geçerli; entity okuması yalnız bu çağrıda VERİLEN grant'ler için yapılır.
|
|
158
|
-
const findings = await lintPolicy(platform, project, environment, body, 'update', policyOverride, { grants: grants ?? [] });
|
|
159
|
-
const drift = unmappedMembers('UpdateAccessPolicyCommand', body);
|
|
160
|
-
const payload = await platform.tenant('PUT', project, `/access/policies/${id}`, { environment, body });
|
|
161
|
-
const notes = carried.length > 0 ? [`Verilmeyen alanlar mevcut kayıttan taşındı: ${carried.join(', ')}.`] : [];
|
|
162
|
-
return attachFindings(withUnmappedMembers(withNormalization(payload, notes), drift), findings);
|
|
163
|
-
}));
|
|
164
|
-
server.registerTool('access_policy_delete', {
|
|
165
|
-
title: 'Erişim politikasını sil',
|
|
166
|
-
description: 'Politikayı grant ve atamalarıyla birlikte siler; etkisi erişim sürümü artınca runtime kararlarına yansır.',
|
|
167
|
-
inputSchema: { project: projectParam, environment: environmentParam, id: guid },
|
|
168
|
-
annotations: destructive
|
|
169
|
-
}, handle(config, ({ project, environment, id }) => platform.tenant('DELETE', project, `/access/policies/${id}`, { environment })));
|
|
170
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, readOnly, writes } from './common.js';
|
|
2
|
-
import { identityHeaders } from '../platform/tokenVault.js';
|
|
3
|
-
import { resourceKeyProblem } from '../knowledge/access.js';
|
|
4
|
-
import { enforcePolicy, attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
5
|
-
import { PlatformApiError } from '../platform/errors.js';
|
|
6
|
-
import { handle } from '../platform/responses.js';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
function asRecord(value) {
|
|
9
|
-
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
10
|
-
}
|
|
11
|
-
function previewNote(data) {
|
|
12
|
-
const trace = Array.isArray(data.trace) ? data.trace.map(asRecord) : [];
|
|
13
|
-
const rowScoped = trace.some(entry => entry.outcome === 'row-scoped')
|
|
14
|
-
? " Kapsamlı deny satır süzer, eylemi kesmez — rowScope metnindeki 'except' parçasına bakın."
|
|
15
|
-
: '';
|
|
16
|
-
if (data.reason === 'allowed-by-override') {
|
|
17
|
-
const endsAt = trace.find(entry => entry.overridesDeny === true && typeof entry.endsAt === 'string')?.endsAt;
|
|
18
|
-
const scope = typeof data.rowScope === 'string' && data.rowScope !== ''
|
|
19
|
-
? ` Satırlar yine şu kapsamla süzülür: ${data.rowScope} — istisna yasağı kaldırır, öteki izinleri daraltmaz.`
|
|
20
|
-
: '';
|
|
21
|
-
return `Kullanıcı istisnası deny'leri aştı; iz 'outcome: overridden' satırlarını gösterir${typeof endsAt === 'string' ? `, bitiş ${endsAt}` : ''}.${scope}${rowScoped}`;
|
|
22
|
-
}
|
|
23
|
-
if (data.isAllowed === true) {
|
|
24
|
-
return (typeof data.rowScope === 'string' && data.rowScope !== ''
|
|
25
|
-
? `İzin var; satırlar şu kapsamla süzülür: ${data.rowScope}. Kararı getiren grant'ler trace'te.`
|
|
26
|
-
: "İzin var. Kararı getiren grant'ler trace'te.") + rowScoped;
|
|
27
|
-
}
|
|
28
|
-
const cuttingDenies = trace.filter(entry => entry.effect === 'deny' && entry.outcome !== 'row-scoped');
|
|
29
|
-
if (cuttingDenies.length > 0) {
|
|
30
|
-
return cuttingDenies.every(entry => entry.scope === 'all')
|
|
31
|
-
? "scope: 'all' taşıyan koşulsuz bir deny grant'i kesiyor (trace'te effect 'deny', outcome 'applied'): koşulsuz 'all' daraltıcı bir kapsam değildir, bu yüzden kapsamsız deny gibi bütün allow'ları ezer (TB-132). Yalnız belli satırları dışlamak istiyorsanız deny'a own · unit · unit-tree · subordinates · function · company kapsamlarından birini ya da 'all' ile birlikte bir koşul verin."
|
|
32
|
-
: "Kapsamsız bir deny grant'i kesiyor (trace'te effect 'deny', outcome 'applied'): kapsamsız deny bütün allow'ları ezer.";
|
|
33
|
-
}
|
|
34
|
-
return (trace.length === 0
|
|
35
|
-
? "Bu kullanıcıya bu izni getiren hiçbir atama yok. Kullanıcının pozisyonunu/rolünü (row_list('user_positions'), user_list) ve politikanın atamasını (access_policy_get) kontrol edin; kaynak anahtarı grant'teki anahtarla aynı mı bakın."
|
|
36
|
-
: "Atama var ama eşleşen allow grant'i yok (izin ya da kaynak anahtarı farklı).") + rowScoped;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* The shortest reveal reason THIS environment accepts. A refused attempt is written to the audit
|
|
40
|
-
* trail and eats the rate limit, so the gateway reads the setting and stops a short reason before
|
|
41
|
-
* it becomes a record (ACC-010). It is read off the runtime access map rather than off the core
|
|
42
|
-
* project row: the platform enforces the environment's own copy, and the two can differ while an
|
|
43
|
-
* environment has not picked the change up. An unreadable map is treated as "no minimum" — the
|
|
44
|
-
* rule must never turn a working reveal into a gateway error.
|
|
45
|
-
*/
|
|
46
|
-
async function revealReasonMinLength(platform, project, environment, application, asUser) {
|
|
47
|
-
try {
|
|
48
|
-
const payload = await platform.tenant('GET', project, `/runtime/${encodeURIComponent(application)}/access`, {
|
|
49
|
-
environment,
|
|
50
|
-
headers: identityHeaders(asUser, undefined, undefined)
|
|
51
|
-
});
|
|
52
|
-
const minLength = Number(asRecord(payload?.data).revealReasonMinLength);
|
|
53
|
-
return Number.isFinite(minLength) && minLength > 0 ? minLength : 0;
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const asUserRefParam = z.string().min(1).describe("Uygulama kullanıcısının kimliği: access_session_create yanıtındaki tok_… referansı ya da ham JWT. Gateway'in servis jetonu bir uygulama kullanıcısı DEĞİLDİR — bu alan olmadan uç uygulama kullanıcısı göremez.");
|
|
60
|
-
export function registerAccessRuntimeTools(server, platform) {
|
|
61
|
-
const config = platform.config;
|
|
62
|
-
server.registerTool('access_preview', {
|
|
63
|
-
title: 'Erişim kararını açıkla',
|
|
64
|
-
description: "Karar motorunun bir uygulama kullanıcısı için vereceği kararı YAN ETKİSİZ açıklar (POST /{project}/access/explain): isAllowed, reason (allowed · allowed-by-override · no-matching-grant · explicit-deny · system), rowScope (satır kapsamının metni; kapsamlı deny 'X except Y' olarak görünür), fieldAccess (alan başına hidden/masked/plain · canWrite · canFilter) ve trace (atama → politika → grant → etki + scope · outcome 'applied'|'row-scoped'|'overridden' · endsAt · overridesDeny). Veri döndürmez, zorlama modunu okumaz ya da değiştirmez. 'Bu kullanıcı neden göremiyor / güncelleyemiyor?' ve gölge moddaki 'reddedilirdi' kayıtlarının cevabı budur. Kavramlar: platform_guide('access') §'Açıklama ve sınama'.",
|
|
65
|
-
inputSchema: {
|
|
66
|
-
project: projectParam,
|
|
67
|
-
environment: environmentParam,
|
|
68
|
-
userId: guid.describe("Uygulama kullanıcısının id'si (users tablosu / user_list)"),
|
|
69
|
-
permission: z.string().min(1).describe("İzin slug'ı (access_permission_list): 'entity.update', 'microservice.execute' …"),
|
|
70
|
-
resourceKey: z.string().min(1).describe("'entity:<entityId>' · 'field:<entityId>:<fieldId>' · 'row:<entityId>:<rowId>' · '<tip>:<id>' ya da yalnız tip ('page')"),
|
|
71
|
-
rowId: z.string().min(1).optional().describe("Satır kapsamını tek bir satır için sınar: satırın cevabı rowInScope ve summary.row'dadır, isAllowed tablonun cevabı olarak kalır")
|
|
72
|
-
},
|
|
73
|
-
annotations: readOnly
|
|
74
|
-
}, handle(config, async ({ project, environment, userId, permission, resourceKey, rowId }) => {
|
|
75
|
-
const problem = resourceKeyProblem(resourceKey);
|
|
76
|
-
if (problem) {
|
|
77
|
-
throw new PlatformApiError(`access_preview: ${problem}. Anahtarları access_resource_list verir; istek gönderilmedi.`, 400);
|
|
78
|
-
}
|
|
79
|
-
const payload = await platform.tenant('POST', project, '/access/explain', { environment, body: { userId, permission, resourceKey, ...(rowId ? { rowId } : {}) } });
|
|
80
|
-
const data = asRecord(payload?.data);
|
|
81
|
-
return {
|
|
82
|
-
...payload,
|
|
83
|
-
summary: {
|
|
84
|
-
decision: data.isAllowed === true ? 'allow' : 'deny',
|
|
85
|
-
...(typeof data.reason === 'string' ? { reason: data.reason } : {}),
|
|
86
|
-
...(typeof data.rowScope === 'string' ? { rowScope: data.rowScope } : {}),
|
|
87
|
-
traceEntries: Array.isArray(data.trace) ? data.trace.length : 0,
|
|
88
|
-
...(typeof data.rowInScope === 'boolean' ? { row: data.rowInScope ? 'in-scope' : 'out-of-scope' } : {})
|
|
89
|
-
},
|
|
90
|
-
note: data.isAllowed === true && data.rowInScope === false
|
|
91
|
-
? `Tabloda izin var ama bu satır kapsam dışında (${typeof data.rowScope === 'string' ? data.rowScope : 'satır kapsamı'}): çalışma zamanında okuma/güncelleme 404 döner. isAllowed tablonun, rowInScope satırın cevabıdır.`
|
|
92
|
-
: previewNote(data)
|
|
93
|
-
};
|
|
94
|
-
}));
|
|
95
|
-
server.registerTool('access_session_create', {
|
|
96
|
-
title: 'Uygulama oturumu aç',
|
|
97
|
-
description: "Kompleks projede uygulama kullanıcısı olarak giriş yapar (POST /{project}/runtime/{application}/auth/sign-in) — korumalı uçları ve sayfaları O KULLANICININ kimliğiyle sınamanın yolu. Erişim jetonu yanıtta maskelenir; tokenRefs'teki tok_… referansını microservice_execute / microservice_debug / page_runtime_get 'asUser' alanına verin (15 dk). Jeton Bearer olarak gider ve platform Bearer'lı isteğe CSRF sormaz; oturum çerezleri gateway'de tutulmaz. Hatalar: 401 AppSessionInvalid (yanlış e-posta/parola), 429 (5 dk'da 10 deneme — tekrar denemeden önce bekleyin). Basit projede giriş uygulamanın kendi microservice ucudur (platform_guide('middleware')).",
|
|
98
|
-
inputSchema: {
|
|
99
|
-
project: projectParam,
|
|
100
|
-
environment: environmentParam,
|
|
101
|
-
application: z.string().min(1).describe("Application slug'ı"),
|
|
102
|
-
email: z.string().min(1),
|
|
103
|
-
password: z.string().min(1).describe('Uygulama kullanıcısının parolası; gateway günlüğüne maskeli yazılır, yanıta girmez'),
|
|
104
|
-
expectFailure: z.boolean().optional().describe('true: hatalı giriş BEKLENİYOR (kasıtlı negatif test)')
|
|
105
|
-
},
|
|
106
|
-
annotations: writes
|
|
107
|
-
}, handle(config, async ({ project, environment, application, email, password }) => {
|
|
108
|
-
// anonymous: gateway kendi jetonunu eklemez ve 401'de jeton yenileyip İKİNCİ bir giriş denemesi
|
|
109
|
-
// yapmaz — yanlış parolanın iki kez sayılması hız sınırını (5 dk'da 10) boşuna tüketirdi.
|
|
110
|
-
const response = await platform.tenantRaw('POST', project, `/runtime/${encodeURIComponent(application)}/auth/sign-in`, { environment, body: { email, password }, anonymous: true });
|
|
111
|
-
const body = asRecord(response.body);
|
|
112
|
-
const data = asRecord(body.data);
|
|
113
|
-
const ok = response.httpStatus < 400;
|
|
114
|
-
const code = typeof body.code === 'string' ? body.code : undefined;
|
|
115
|
-
return {
|
|
116
|
-
httpStatus: response.httpStatus,
|
|
117
|
-
body: response.body,
|
|
118
|
-
...(ok ? { session: { user: data.user, accessExpiresAt: data.accessTokenExpiresAt, refreshExpiresAt: data.refreshTokenExpiresAt } } : {}),
|
|
119
|
-
note: ok
|
|
120
|
-
? "Oturum açıldı. tokenRefs içindeki 'body.data.accessToken' referansını asUser olarak verin; 15 dk sonra yeniden giriş gerekir. Kullanıcının yetkisi atamalarından gelir — erişemediği bir şey için access_preview."
|
|
121
|
-
: code === 'AppSessionRateLimitedException'
|
|
122
|
-
? 'Giriş hız sınırına takıldı (5 dk\'da 10 deneme). Beklemeden tekrar denemeyin.'
|
|
123
|
-
: code === 'AppSignInFailedException'
|
|
124
|
-
? "E-posta ya da parola yanlış (ya da kullanıcı bu projenin 'users' tablosunda yok). Yeni projede users boş gelir — platform_guide('getting-started') §'Kompleks projede ilk kullanıcı'."
|
|
125
|
-
: 'Giriş reddedildi; gövdedeki code/message nedeni söyler.'
|
|
126
|
-
};
|
|
127
|
-
}));
|
|
128
|
-
server.registerTool('access_runtime_get', {
|
|
129
|
-
title: 'Uygulama kullanıcısının erişim haritası',
|
|
130
|
-
description: "Giriş yapmış bir uygulama kullanıcısının O ANDAKİ erişim haritasını getirir (GET /{project}/runtime/{application}/access): zorlama modu, görebildiği sayfa slug'ları, çağırabildiği microservice slug'ları ve entity başına eylemler (read · create · update · delete …) + alan erişimleri (hidden/masked/plain · canWrite · canFilter). Uygulama menüsünü/butonlarını buna göre çizer. access_preview'dan farkı: o TEK bir kararı açıklar, bu kullanıcının bütün yüzeyini verir. asUser ZORUNLUDUR — bu uç gateway'in servis jetonuyla çağrılırsa uygulama kullanıcısı yoktur ve harita boş döner.",
|
|
131
|
-
inputSchema: {
|
|
132
|
-
project: projectParam,
|
|
133
|
-
environment: environmentParam,
|
|
134
|
-
application: z.string().min(1).describe("Application slug'ı"),
|
|
135
|
-
asUser: asUserRefParam
|
|
136
|
-
},
|
|
137
|
-
annotations: readOnly
|
|
138
|
-
}, handle(config, async ({ project, environment, application, asUser }) => {
|
|
139
|
-
const payload = await platform.tenant('GET', project, `/runtime/${encodeURIComponent(application)}/access`, { environment, headers: identityHeaders(asUser, undefined, undefined) });
|
|
140
|
-
const data = asRecord(payload?.data);
|
|
141
|
-
return {
|
|
142
|
-
...payload,
|
|
143
|
-
summary: {
|
|
144
|
-
mode: data.mode,
|
|
145
|
-
pages: Array.isArray(data.pages) ? data.pages.length : 0,
|
|
146
|
-
microservices: Array.isArray(data.microservices) ? data.microservices.length : 0,
|
|
147
|
-
entities: Array.isArray(data.entities) ? data.entities.length : 0
|
|
148
|
-
},
|
|
149
|
-
note: data.mode === 'off'
|
|
150
|
-
? "Zorlama KAPALI: harita her şeyi açık gösterir çünkü karar motoru çalışmıyor. Gerçek kapsamı görmek için environment'ı shadow/enforce'a alın (access_enforcement_mode_set)."
|
|
151
|
-
: "Harita bu kullanıcının o andaki yetkisidir; bir öğe eksikse nedenini access_preview söyler."
|
|
152
|
-
};
|
|
153
|
-
}));
|
|
154
|
-
server.registerTool('access_reveal', {
|
|
155
|
-
title: 'Maskeli alan değerini aç',
|
|
156
|
-
description: "Maskelenmiş (korumalı) bir alanın açık değerini TEK BİR SATIR için okur (POST /{project}/runtime/{application}/reveal). Kullanıcının o alanda 'field.reveal' izni olmalıdır; her çağrı GEREKÇESİYLE BİRLİKTE denetim izine yazılır ve hız sınırına tabidir (429). Maskeli değerin açığını okumanın başka yolu yoktur — row_list korumalı alanı maskeli döner. asUser zorunludur: gerekçe ve iz o kullanıcıya yazılır. Projenin revealReasonMinLength ayarı 0'dan büyükse daha kısa bir gerekçe 400 RevealReasonTooShort alır, hız sınırını tüketir ve denetim defterine REVEAL-DENIED düşer — gateway bunu çağrı gitmeden durdurur (ACC-010).",
|
|
157
|
-
inputSchema: {
|
|
158
|
-
project: projectParam,
|
|
159
|
-
environment: environmentParam,
|
|
160
|
-
application: z.string().min(1).describe("Application slug'ı"),
|
|
161
|
-
asUser: asUserRefParam,
|
|
162
|
-
entityId: guid,
|
|
163
|
-
rowId: z.string().min(1).describe("Satırın birincil anahtar değeri"),
|
|
164
|
-
fieldSlug: z.string().min(1).describe("Alanın slug'ı (entity_get)"),
|
|
165
|
-
reason: z.string().min(1).max(1000).describe("Denetim izine yazılacak gerekçe; zorunludur ve boş bırakılamaz. Projenin revealReasonMinLength ayarından kısa olamaz (project_get / access_runtime_get)"),
|
|
166
|
-
expectFailure: z.boolean().optional().describe('true: reddedilmesi BEKLENİYOR (kasıtlı negatif test) — ACC-010 ön denetimi de atlanır')
|
|
167
|
-
},
|
|
168
|
-
annotations: writes
|
|
169
|
-
}, handle(config, async ({ project, environment, application, asUser, entityId, rowId, fieldSlug, reason, expectFailure }) => {
|
|
170
|
-
let findings = [];
|
|
171
|
-
if (expectFailure !== true && config.policyMode !== 'off') {
|
|
172
|
-
const minLength = await revealReasonMinLength(platform, project, environment, application, asUser);
|
|
173
|
-
if (reason.trim().length < minLength) {
|
|
174
|
-
findings = enforcePolicy(applyPolicyMode(config, [{
|
|
175
|
-
ruleId: 'ACC-010',
|
|
176
|
-
severity: 'block',
|
|
177
|
-
message: `Gerekçe ${reason.trim().length} karakter, bu ortam en az ${minLength} istiyor (access_runtime_get → revealReasonMinLength). Platform bunu 400 RevealReasonTooShort ile reddeder, hız sınırını tüketir ve denetim defterine REVEAL-DENIED yazar — istek gönderilmedi. Kasıtlı olumsuz ölçüm yapıyorsanız expectFailure: true verin.`,
|
|
178
|
-
guide: 'ngapps://guides/access'
|
|
179
|
-
}]), false);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const response = await platform.tenantRaw('POST', project, `/runtime/${encodeURIComponent(application)}/reveal`, {
|
|
183
|
-
environment,
|
|
184
|
-
body: { entityId, rowId, fieldSlug, reason },
|
|
185
|
-
headers: identityHeaders(asUser, undefined, undefined)
|
|
186
|
-
});
|
|
187
|
-
const body = asRecord(response.body);
|
|
188
|
-
const code = typeof body.code === 'string' ? body.code : undefined;
|
|
189
|
-
return attachFindings({
|
|
190
|
-
httpStatus: response.httpStatus,
|
|
191
|
-
body: response.body,
|
|
192
|
-
note: response.httpStatus < 400
|
|
193
|
-
? 'Değer açıldı ve çağrı denetim izine gerekçesiyle yazıldı.'
|
|
194
|
-
: code === 'AccessRevealDeniedException'
|
|
195
|
-
? "Kullanıcının bu alanda 'field.reveal' izni yok: grant'i access_matrix_get ile kontrol edin, kararı access_preview açıklar."
|
|
196
|
-
: typeof body.message === 'string' && body.message.includes('RevealReasonTooShort')
|
|
197
|
-
? 'Gerekçe projenin asgari uzunluğunun altında (project_get → revealReasonMinLength); deneme REVEAL-DENIED olarak kaydedildi ve hız sınırını tüketti.'
|
|
198
|
-
: response.httpStatus === 429
|
|
199
|
-
? 'Açma hız sınırına takıldı; beklemeden tekrar denemeyin.'
|
|
200
|
-
: 'Açma reddedildi; gövdedeki code/message nedeni söyler.'
|
|
201
|
-
}, findings);
|
|
202
|
-
}));
|
|
203
|
-
}
|