@company-semantics/contracts 2.17.0 → 3.0.0
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/package.json +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +177 -4
- package/src/content/schemas.ts +0 -1
- package/src/generated/openapi-routes.ts +4 -0
- package/src/org/company-md.ts +0 -4
- package/src/permissions/index.ts +1 -0
- package/src/permissions/permission-introspection.ts +85 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '30c4d8eb9dce' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '30c4d8eb9dcefed99d44a531c27a9e5a25eb4842c578e8933f64678a13816d07' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -2570,6 +2570,57 @@ export interface paths {
|
|
|
2570
2570
|
patch?: never;
|
|
2571
2571
|
trace?: never;
|
|
2572
2572
|
};
|
|
2573
|
+
"/api/{entityType}/{id}/who-can-access": {
|
|
2574
|
+
parameters: {
|
|
2575
|
+
query?: never;
|
|
2576
|
+
header?: never;
|
|
2577
|
+
path?: never;
|
|
2578
|
+
cookie?: never;
|
|
2579
|
+
};
|
|
2580
|
+
/** List every principal with effective access to an entity (grouped by source) */
|
|
2581
|
+
get: operations["getEntityWhoCanAccess"];
|
|
2582
|
+
put?: never;
|
|
2583
|
+
post?: never;
|
|
2584
|
+
delete?: never;
|
|
2585
|
+
options?: never;
|
|
2586
|
+
head?: never;
|
|
2587
|
+
patch?: never;
|
|
2588
|
+
trace?: never;
|
|
2589
|
+
};
|
|
2590
|
+
"/api/{entityType}/{id}/recent-decisions": {
|
|
2591
|
+
parameters: {
|
|
2592
|
+
query?: never;
|
|
2593
|
+
header?: never;
|
|
2594
|
+
path?: never;
|
|
2595
|
+
cookie?: never;
|
|
2596
|
+
};
|
|
2597
|
+
/** Newest-first permission_decisions rows for an entity (introspection timeline) */
|
|
2598
|
+
get: operations["getEntityRecentDecisions"];
|
|
2599
|
+
put?: never;
|
|
2600
|
+
post?: never;
|
|
2601
|
+
delete?: never;
|
|
2602
|
+
options?: never;
|
|
2603
|
+
head?: never;
|
|
2604
|
+
patch?: never;
|
|
2605
|
+
trace?: never;
|
|
2606
|
+
};
|
|
2607
|
+
"/api/me/access-list": {
|
|
2608
|
+
parameters: {
|
|
2609
|
+
query?: never;
|
|
2610
|
+
header?: never;
|
|
2611
|
+
path?: never;
|
|
2612
|
+
cookie?: never;
|
|
2613
|
+
};
|
|
2614
|
+
/** Every effective grant for the calling user — "what can I access?" */
|
|
2615
|
+
get: operations["getMyAccessList"];
|
|
2616
|
+
put?: never;
|
|
2617
|
+
post?: never;
|
|
2618
|
+
delete?: never;
|
|
2619
|
+
options?: never;
|
|
2620
|
+
head?: never;
|
|
2621
|
+
patch?: never;
|
|
2622
|
+
trace?: never;
|
|
2623
|
+
};
|
|
2573
2624
|
"/api/internal-admin/impersonate/start": {
|
|
2574
2625
|
parameters: {
|
|
2575
2626
|
query?: never;
|
|
@@ -3539,10 +3590,6 @@ export interface components {
|
|
|
3539
3590
|
id: string;
|
|
3540
3591
|
name: string;
|
|
3541
3592
|
} | null;
|
|
3542
|
-
coOwners: {
|
|
3543
|
-
id: string;
|
|
3544
|
-
name: string;
|
|
3545
|
-
}[];
|
|
3546
3593
|
canEdit: boolean;
|
|
3547
3594
|
inheritsFrom: string | null;
|
|
3548
3595
|
sources: {
|
|
@@ -4534,6 +4581,50 @@ export interface components {
|
|
|
4534
4581
|
id: string;
|
|
4535
4582
|
};
|
|
4536
4583
|
};
|
|
4584
|
+
WhoCanAccessResponse: {
|
|
4585
|
+
entity_type: string;
|
|
4586
|
+
/** Format: uuid */
|
|
4587
|
+
entity_id: string;
|
|
4588
|
+
principals: {
|
|
4589
|
+
principal: {
|
|
4590
|
+
/** @enum {string} */
|
|
4591
|
+
type: "user" | "unit" | "org";
|
|
4592
|
+
/** Format: uuid */
|
|
4593
|
+
id: string;
|
|
4594
|
+
};
|
|
4595
|
+
/** @enum {string} */
|
|
4596
|
+
access_level: "owner" | "editor" | "commenter" | "viewer";
|
|
4597
|
+
/** @enum {string} */
|
|
4598
|
+
source: "explicit" | "ownership" | "visibility" | "inheritance" | "migration";
|
|
4599
|
+
}[];
|
|
4600
|
+
};
|
|
4601
|
+
RecentDecisionsResponse: {
|
|
4602
|
+
entity_type: string;
|
|
4603
|
+
/** Format: uuid */
|
|
4604
|
+
entity_id: string;
|
|
4605
|
+
rows: {
|
|
4606
|
+
/** Format: uuid */
|
|
4607
|
+
id: string;
|
|
4608
|
+
policy_name: string;
|
|
4609
|
+
actor_user_id: string | null;
|
|
4610
|
+
allow: boolean;
|
|
4611
|
+
reason: string;
|
|
4612
|
+
source_chain: ("explicit" | "ownership" | "visibility" | "inheritance" | "migration")[];
|
|
4613
|
+
/** Format: date-time */
|
|
4614
|
+
decided_at: string;
|
|
4615
|
+
}[];
|
|
4616
|
+
};
|
|
4617
|
+
MyAccessListResponse: {
|
|
4618
|
+
entries: {
|
|
4619
|
+
entity_type: string;
|
|
4620
|
+
/** Format: uuid */
|
|
4621
|
+
entity_id: string;
|
|
4622
|
+
/** @enum {string} */
|
|
4623
|
+
access_level: "owner" | "editor" | "commenter" | "viewer";
|
|
4624
|
+
/** @enum {string} */
|
|
4625
|
+
source: "explicit" | "ownership" | "visibility" | "inheritance" | "migration";
|
|
4626
|
+
}[];
|
|
4627
|
+
};
|
|
4537
4628
|
ImpersonationSessionResponse: {
|
|
4538
4629
|
session: {
|
|
4539
4630
|
impersonationSessionId: string;
|
|
@@ -8801,6 +8892,88 @@ export interface operations {
|
|
|
8801
8892
|
};
|
|
8802
8893
|
};
|
|
8803
8894
|
};
|
|
8895
|
+
getEntityWhoCanAccess: {
|
|
8896
|
+
parameters: {
|
|
8897
|
+
query?: never;
|
|
8898
|
+
header?: never;
|
|
8899
|
+
path: {
|
|
8900
|
+
entityType: string;
|
|
8901
|
+
id: string;
|
|
8902
|
+
};
|
|
8903
|
+
cookie?: never;
|
|
8904
|
+
};
|
|
8905
|
+
requestBody?: never;
|
|
8906
|
+
responses: {
|
|
8907
|
+
/** @description Effective grants for the entity — one element per (principal, source) row in effective_acl_grants */
|
|
8908
|
+
200: {
|
|
8909
|
+
headers: {
|
|
8910
|
+
[name: string]: unknown;
|
|
8911
|
+
};
|
|
8912
|
+
content: {
|
|
8913
|
+
"application/json": components["schemas"]["WhoCanAccessResponse"];
|
|
8914
|
+
};
|
|
8915
|
+
};
|
|
8916
|
+
/** @description Entity not found or caller lacks View on the entity (no-leak — the deny path 404s like the missing-entity path) */
|
|
8917
|
+
404: {
|
|
8918
|
+
headers: {
|
|
8919
|
+
[name: string]: unknown;
|
|
8920
|
+
};
|
|
8921
|
+
content?: never;
|
|
8922
|
+
};
|
|
8923
|
+
};
|
|
8924
|
+
};
|
|
8925
|
+
getEntityRecentDecisions: {
|
|
8926
|
+
parameters: {
|
|
8927
|
+
query?: {
|
|
8928
|
+
limit?: number;
|
|
8929
|
+
};
|
|
8930
|
+
header?: never;
|
|
8931
|
+
path: {
|
|
8932
|
+
entityType: string;
|
|
8933
|
+
id: string;
|
|
8934
|
+
};
|
|
8935
|
+
cookie?: never;
|
|
8936
|
+
};
|
|
8937
|
+
requestBody?: never;
|
|
8938
|
+
responses: {
|
|
8939
|
+
/** @description Newest-first permission_decisions rows for the entity (reason verbatim) */
|
|
8940
|
+
200: {
|
|
8941
|
+
headers: {
|
|
8942
|
+
[name: string]: unknown;
|
|
8943
|
+
};
|
|
8944
|
+
content: {
|
|
8945
|
+
"application/json": components["schemas"]["RecentDecisionsResponse"];
|
|
8946
|
+
};
|
|
8947
|
+
};
|
|
8948
|
+
/** @description Entity not found or caller lacks View on the entity (no-leak — the deny path 404s like the missing-entity path) */
|
|
8949
|
+
404: {
|
|
8950
|
+
headers: {
|
|
8951
|
+
[name: string]: unknown;
|
|
8952
|
+
};
|
|
8953
|
+
content?: never;
|
|
8954
|
+
};
|
|
8955
|
+
};
|
|
8956
|
+
};
|
|
8957
|
+
getMyAccessList: {
|
|
8958
|
+
parameters: {
|
|
8959
|
+
query?: never;
|
|
8960
|
+
header?: never;
|
|
8961
|
+
path?: never;
|
|
8962
|
+
cookie?: never;
|
|
8963
|
+
};
|
|
8964
|
+
requestBody?: never;
|
|
8965
|
+
responses: {
|
|
8966
|
+
/** @description Effective grants for the calling user across all sources */
|
|
8967
|
+
200: {
|
|
8968
|
+
headers: {
|
|
8969
|
+
[name: string]: unknown;
|
|
8970
|
+
};
|
|
8971
|
+
content: {
|
|
8972
|
+
"application/json": components["schemas"]["MyAccessListResponse"];
|
|
8973
|
+
};
|
|
8974
|
+
};
|
|
8975
|
+
};
|
|
8976
|
+
};
|
|
8804
8977
|
startImpersonation: {
|
|
8805
8978
|
parameters: {
|
|
8806
8979
|
query?: never;
|
package/src/content/schemas.ts
CHANGED
|
@@ -66,7 +66,6 @@ export const CompanyMdDocResponseSchema = z.object({
|
|
|
66
66
|
parentId: z.string().nullable(),
|
|
67
67
|
owningUnitId: z.string().nullable(),
|
|
68
68
|
owner: CompanyMdPersonSchema.nullable(),
|
|
69
|
-
coOwners: z.array(CompanyMdPersonSchema),
|
|
70
69
|
canEdit: z.boolean(),
|
|
71
70
|
inheritsFrom: z.string().nullable(),
|
|
72
71
|
sources: z.array(CompanyMdSourceSchema),
|
|
@@ -50,6 +50,7 @@ export const openApiRoutes = {
|
|
|
50
50
|
'/api/internal-admin/impersonate/session': ['GET'],
|
|
51
51
|
'/api/internal-admin/impersonate/start': ['POST'],
|
|
52
52
|
'/api/me': ['GET'],
|
|
53
|
+
'/api/me/access-list': ['GET'],
|
|
53
54
|
'/api/me/meetings/recordings/start': ['POST'],
|
|
54
55
|
'/api/me/meetings/recordings/{id}': ['DELETE'],
|
|
55
56
|
'/api/me/meetings/recordings/{id}/complete': ['POST'],
|
|
@@ -75,6 +76,7 @@ export const openApiRoutes = {
|
|
|
75
76
|
'/api/org-units/{unitId}/memberships': ['GET', 'POST'],
|
|
76
77
|
'/api/org-units/{unitId}/memberships/{userId}': ['DELETE'],
|
|
77
78
|
'/api/org-units/{unitId}/memberships/{userId}/role': ['PUT'],
|
|
79
|
+
'/api/org-units/{unitId}/my-authority': ['GET'],
|
|
78
80
|
'/api/org-units/{unitId}/owners': ['GET'],
|
|
79
81
|
'/api/org-units/{unitId}/permissions': ['GET'],
|
|
80
82
|
'/api/org-units/{unitId}/relationships': ['GET', 'POST'],
|
|
@@ -143,8 +145,10 @@ export const openApiRoutes = {
|
|
|
143
145
|
'/api/{entityType}/{id}/acl': ['GET'],
|
|
144
146
|
'/api/{entityType}/{id}/acl/{principalType}/{principalId}': ['DELETE', 'PUT'],
|
|
145
147
|
'/api/{entityType}/{id}/effective-access': ['POST'],
|
|
148
|
+
'/api/{entityType}/{id}/recent-decisions': ['GET'],
|
|
146
149
|
'/api/{entityType}/{id}/transfer-ownership': ['POST'],
|
|
147
150
|
'/api/{entityType}/{id}/visibility': ['PATCH'],
|
|
151
|
+
'/api/{entityType}/{id}/who-can-access': ['GET'],
|
|
148
152
|
'/auth/logout': ['POST'],
|
|
149
153
|
'/auth/me': ['GET'],
|
|
150
154
|
'/auth/sso/callback': ['GET', 'POST'],
|
package/src/org/company-md.ts
CHANGED
|
@@ -123,10 +123,6 @@ export interface CompanyMdDocCore extends CompanyMdNodeIdentity {
|
|
|
123
123
|
|
|
124
124
|
export interface CompanyMdDocCollaborators {
|
|
125
125
|
readonly owner: { readonly id: string; readonly name: string } | null;
|
|
126
|
-
readonly coOwners: ReadonlyArray<{
|
|
127
|
-
readonly id: string;
|
|
128
|
-
readonly name: string;
|
|
129
|
-
}>;
|
|
130
126
|
readonly canEdit: boolean;
|
|
131
127
|
readonly members: ReadonlyArray<{
|
|
132
128
|
readonly id: string;
|
package/src/permissions/index.ts
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Introspection API Vocabulary
|
|
3
|
+
*
|
|
4
|
+
* Zod schemas for the AUTH-010 introspection surfaces (PRD-00678): three
|
|
5
|
+
* read-only GET routes over the AUTH-002 effective_acl_grants projection and
|
|
6
|
+
* the AUTH-006 permission_decisions audit sink.
|
|
7
|
+
*
|
|
8
|
+
* Backend routes:
|
|
9
|
+
* - GET /api/{entityType}/{id}/who-can-access
|
|
10
|
+
* - GET /api/{entityType}/{id}/recent-decisions
|
|
11
|
+
* - GET /api/me/access-list
|
|
12
|
+
*
|
|
13
|
+
* No new tables or policies — every read goes through existing infrastructure
|
|
14
|
+
* + the AUTH-006 evaluate() + assertAllowed() gate (entity-scoped routes).
|
|
15
|
+
*
|
|
16
|
+
* Per `feedback-decision-logic-must-be-visible`: reason strings + source chains
|
|
17
|
+
* flow verbatim to the UI. The schema mirrors what the evaluator records into
|
|
18
|
+
* permission_decisions; the UI never re-derives.
|
|
19
|
+
*/
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
import { AccessLevelSchema } from './access-levels';
|
|
22
|
+
import { AccessSourceSchema } from './access-source';
|
|
23
|
+
import { PrincipalSchema } from './share-api';
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// WhoCanAccess — every principal with effective access to an entity.
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export const WhoCanAccessPrincipalSchema = z.object({
|
|
30
|
+
principal: PrincipalSchema,
|
|
31
|
+
access_level: AccessLevelSchema,
|
|
32
|
+
source: AccessSourceSchema,
|
|
33
|
+
});
|
|
34
|
+
export type WhoCanAccessPrincipal = z.infer<typeof WhoCanAccessPrincipalSchema>;
|
|
35
|
+
|
|
36
|
+
export const WhoCanAccessResponseSchema = z.object({
|
|
37
|
+
entity_type: z.string(),
|
|
38
|
+
entity_id: z.string().uuid(),
|
|
39
|
+
principals: z.array(WhoCanAccessPrincipalSchema),
|
|
40
|
+
});
|
|
41
|
+
export type WhoCanAccessResponse = z.infer<typeof WhoCanAccessResponseSchema>;
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// RecentDecisions — newest-first permission_decisions rows for an entity.
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
export const RecentDecisionSchema = z.object({
|
|
48
|
+
id: z.string().uuid(),
|
|
49
|
+
policy_name: z.string(),
|
|
50
|
+
actor_user_id: z.string().uuid().nullable(),
|
|
51
|
+
allow: z.boolean(),
|
|
52
|
+
reason: z.string(),
|
|
53
|
+
source_chain: z.array(AccessSourceSchema),
|
|
54
|
+
decided_at: z.string().datetime(),
|
|
55
|
+
});
|
|
56
|
+
export type RecentDecision = z.infer<typeof RecentDecisionSchema>;
|
|
57
|
+
|
|
58
|
+
export const RecentDecisionsResponseSchema = z.object({
|
|
59
|
+
entity_type: z.string(),
|
|
60
|
+
entity_id: z.string().uuid(),
|
|
61
|
+
rows: z.array(RecentDecisionSchema),
|
|
62
|
+
});
|
|
63
|
+
export type RecentDecisionsResponse = z.infer<typeof RecentDecisionsResponseSchema>;
|
|
64
|
+
|
|
65
|
+
export const RecentDecisionsQuerySchema = z.object({
|
|
66
|
+
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
67
|
+
});
|
|
68
|
+
export type RecentDecisionsQuery = z.infer<typeof RecentDecisionsQuerySchema>;
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// MyAccessList — every effective row for the calling user's principal.
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
export const MyAccessEntrySchema = z.object({
|
|
75
|
+
entity_type: z.string(),
|
|
76
|
+
entity_id: z.string().uuid(),
|
|
77
|
+
access_level: AccessLevelSchema,
|
|
78
|
+
source: AccessSourceSchema,
|
|
79
|
+
});
|
|
80
|
+
export type MyAccessEntry = z.infer<typeof MyAccessEntrySchema>;
|
|
81
|
+
|
|
82
|
+
export const MyAccessListResponseSchema = z.object({
|
|
83
|
+
entries: z.array(MyAccessEntrySchema),
|
|
84
|
+
});
|
|
85
|
+
export type MyAccessListResponse = z.infer<typeof MyAccessListResponseSchema>;
|