@checkstack/catalog-common 0.0.3 → 1.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,179 @@
1
1
  # @checkstack/catalog-common
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
8
+
9
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
10
+
11
+ ## Changes
12
+
13
+ ### Core Infrastructure (`@checkstack/common`)
14
+
15
+ - Introduced `AccessRule` interface as the primary access control type
16
+ - Added `accessPair()` helper for creating read/manage access rule pairs
17
+ - Added `access()` builder for individual access rules
18
+ - Replaced `Permission` type with `AccessRule` throughout
19
+
20
+ ### API Changes
21
+
22
+ - `env.registerPermissions()` → `env.registerAccessRules()`
23
+ - `meta.permissions` → `meta.access` in RPC contracts
24
+ - `usePermission()` → `useAccess()` in frontend hooks
25
+ - Route `permission:` field → `accessRule:` field
26
+
27
+ ### UI Changes
28
+
29
+ - "Roles & Permissions" tab → "Roles & Access Rules"
30
+ - "You don't have permission..." → "You don't have access..."
31
+ - All permission-related UI text updated
32
+
33
+ ### Documentation & Templates
34
+
35
+ - Updated 18 documentation files with AccessRule terminology
36
+ - Updated 7 scaffolding templates with `accessPair()` pattern
37
+ - All code examples use new AccessRule API
38
+
39
+ ## Migration Guide
40
+
41
+ ### Backend Plugins
42
+
43
+ ```diff
44
+ - import { permissionList } from "./permissions";
45
+ - env.registerPermissions(permissionList);
46
+ + import { accessRules } from "./access";
47
+ + env.registerAccessRules(accessRules);
48
+ ```
49
+
50
+ ### RPC Contracts
51
+
52
+ ```diff
53
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
54
+ + .meta({ userType: "user", access: [access.read] })
55
+ ```
56
+
57
+ ### Frontend Hooks
58
+
59
+ ```diff
60
+ - const canRead = accessApi.usePermission(permissions.read.id);
61
+ + const canRead = accessApi.useAccess(access.read);
62
+ ```
63
+
64
+ ### Routes
65
+
66
+ ```diff
67
+ - permission: permissions.entityRead.id,
68
+ + accessRule: access.read,
69
+ ```
70
+
71
+ ### Patch Changes
72
+
73
+ - Updated dependencies [9faec1f]
74
+ - Updated dependencies [f533141]
75
+ - @checkstack/common@0.2.0
76
+ - @checkstack/frontend-api@0.1.0
77
+
78
+ ## 1.0.0
79
+
80
+ ### Major Changes
81
+
82
+ - 8e43507: BREAKING: `getSystems` now returns `{ systems: [...] }` instead of plain array
83
+
84
+ This change enables resource-level access control filtering for the catalog plugin. The middleware needs a consistent object format with named keys to perform post-execution filtering on list endpoints.
85
+
86
+ ## Breaking Changes
87
+
88
+ - `getSystems()` now returns `{ systems: System[] }` instead of `System[]`
89
+ - All call sites must update to destructure: `const { systems } = await api.getSystems()`
90
+
91
+ ## New Features
92
+
93
+ - Added `resourceAccess` metadata to catalog endpoints:
94
+ - `getSystems`: List filtering by team access
95
+ - `getSystem`: Single resource pre-check by team access
96
+ - `getEntities`: List filtering for systems by team access
97
+
98
+ ## Migration
99
+
100
+ ```diff
101
+ - const systems = await catalogApi.getSystems();
102
+ + const { systems } = await catalogApi.getSystems();
103
+ ```
104
+
105
+ ### Minor Changes
106
+
107
+ - 8e43507: # Teams and Resource-Level Access Control
108
+
109
+ This release introduces a comprehensive Teams system for organizing users and controlling access to resources at a granular level.
110
+
111
+ ## Features
112
+
113
+ ### Team Management
114
+
115
+ - Create, update, and delete teams with name and description
116
+ - Add/remove users from teams
117
+ - Designate team managers with elevated privileges
118
+ - View team membership and manager status
119
+
120
+ ### Resource-Level Access Control
121
+
122
+ - Grant teams access to specific resources (systems, health checks, incidents, maintenances)
123
+ - Configure read-only or manage permissions per team
124
+ - Resource-level "Team Only" mode that restricts access exclusively to team members
125
+ - Separate `resourceAccessSettings` table for resource-level settings (not per-grant)
126
+ - Automatic cleanup of grants when teams are deleted (database cascade)
127
+
128
+ ### Middleware Integration
129
+
130
+ - Extended `autoAuthMiddleware` to support resource access checks
131
+ - Single-resource pre-handler validation for detail endpoints
132
+ - Automatic list filtering for collection endpoints
133
+ - S2S endpoints for access verification
134
+
135
+ ### Frontend Components
136
+
137
+ - `TeamsTab` component for managing teams in Auth Settings
138
+ - `TeamAccessEditor` component for assigning team access to resources
139
+ - Resource-level "Team Only" toggle in `TeamAccessEditor`
140
+ - Integration into System, Health Check, Incident, and Maintenance editors
141
+
142
+ ## Breaking Changes
143
+
144
+ ### API Response Format Changes
145
+
146
+ List endpoints now return objects with named keys instead of arrays directly:
147
+
148
+ ```typescript
149
+ // Before
150
+ const systems = await catalogApi.getSystems();
151
+
152
+ // After
153
+ const { systems } = await catalogApi.getSystems();
154
+ ```
155
+
156
+ Affected endpoints:
157
+
158
+ - `catalog.getSystems` → `{ systems: [...] }`
159
+ - `healthcheck.getConfigurations` → `{ configurations: [...] }`
160
+ - `incident.listIncidents` → `{ incidents: [...] }`
161
+ - `maintenance.listMaintenances` → `{ maintenances: [...] }`
162
+
163
+ ### User Identity Enrichment
164
+
165
+ `RealUser` and `ApplicationUser` types now include `teamIds: string[]` field with team memberships.
166
+
167
+ ## Documentation
168
+
169
+ See `docs/backend/teams.md` for complete API reference and integration guide.
170
+
171
+ ### Patch Changes
172
+
173
+ - Updated dependencies [8e43507]
174
+ - @checkstack/common@0.1.0
175
+ - @checkstack/frontend-api@0.0.4
176
+
3
177
  ## 0.0.3
4
178
 
5
179
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/catalog-common",
3
- "version": "0.0.3",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/access.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { accessPair } from "@checkstack/common";
2
+
3
+ /**
4
+ * Access rules for the Catalog plugin.
5
+ *
6
+ * Systems have instance-level access control (team-based filtering).
7
+ * Groups and views are global (no team-based filtering).
8
+ */
9
+ export const catalogAccess = {
10
+ /**
11
+ * System access with team-based filtering.
12
+ * - Read: View systems (filtered by team grants if no global access)
13
+ * - Manage: Create, update, delete systems
14
+ */
15
+ system: accessPair(
16
+ "system",
17
+ {
18
+ read: "View systems in catalog",
19
+ manage: "Create, update, and delete systems",
20
+ },
21
+ {
22
+ idParam: "systemId",
23
+ listKey: "systems",
24
+ readIsDefault: true,
25
+ readIsPublic: true,
26
+ }
27
+ ),
28
+
29
+ /**
30
+ * Group access (global, no team-based filtering).
31
+ */
32
+ group: accessPair(
33
+ "group",
34
+ {
35
+ read: "View groups",
36
+ manage: "Create, update, and delete groups",
37
+ },
38
+ {
39
+ readIsDefault: true,
40
+ readIsPublic: true,
41
+ }
42
+ ),
43
+
44
+ /**
45
+ * View access (global, user-only).
46
+ */
47
+ view: accessPair(
48
+ "view",
49
+ {
50
+ read: "View saved views",
51
+ manage: "Manage saved views",
52
+ },
53
+ {
54
+ readIsDefault: true,
55
+ }
56
+ ),
57
+ };
58
+
59
+ /**
60
+ * All access rules for registration with the plugin system.
61
+ */
62
+ export const catalogAccessRules = [
63
+ catalogAccess.system.read,
64
+ catalogAccess.system.manage,
65
+ catalogAccess.group.read,
66
+ catalogAccess.group.manage,
67
+ catalogAccess.view.read,
68
+ catalogAccess.view.manage,
69
+ ];
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { permissions, permissionList } from "./permissions";
1
+ export { catalogAccess, catalogAccessRules } from "./access";
2
2
  export * from "./rpc-contract";
3
3
  export * from "./types";
4
4
  export * from "./slots";
@@ -6,7 +6,7 @@ import {
6
6
  import { pluginMetadata } from "./plugin-metadata";
7
7
  import { z } from "zod";
8
8
  import { SystemSchema, GroupSchema, ViewSchema } from "./types";
9
- import { permissions } from "./permissions";
9
+ import { catalogAccess } from "./access";
10
10
 
11
11
  // Base builder with full metadata support
12
12
  const _base = oc.$meta<ProcedureMetadata>({});
@@ -51,11 +51,14 @@ const CreateViewInputSchema = z.object({
51
51
  // Catalog RPC Contract using oRPC's contract-first pattern
52
52
  export const catalogContract = {
53
53
  // ==========================================================================
54
- // ENTITY READ ENDPOINTS (userType: "public" - accessible by anyone with permission)
54
+ // ENTITY READ ENDPOINTS (userType: "public" - accessible by anyone with access)
55
55
  // ==========================================================================
56
56
 
57
57
  getEntities: _base
58
- .meta({ userType: "public", permissions: [permissions.catalogRead.id] })
58
+ .meta({
59
+ userType: "public",
60
+ access: [catalogAccess.system.read],
61
+ })
59
62
  .output(
60
63
  z.object({
61
64
  systems: z.array(SystemSchema),
@@ -64,26 +67,32 @@ export const catalogContract = {
64
67
  ),
65
68
 
66
69
  getSystems: _base
67
- .meta({ userType: "public", permissions: [permissions.catalogRead.id] })
68
- .output(z.array(SystemSchema)),
70
+ .meta({
71
+ userType: "public",
72
+ access: [catalogAccess.system.read],
73
+ })
74
+ .output(z.object({ systems: z.array(SystemSchema) })),
69
75
 
70
76
  getSystem: _base
71
- .meta({ userType: "public", permissions: [permissions.catalogRead.id] })
77
+ .meta({
78
+ userType: "public",
79
+ access: [catalogAccess.system.read],
80
+ })
72
81
  .input(z.object({ systemId: z.string() }))
73
82
  .output(SystemSchema.nullable()),
74
83
 
75
84
  getGroups: _base
76
- .meta({ userType: "public", permissions: [permissions.catalogRead.id] })
85
+ .meta({ userType: "public", access: [catalogAccess.group.read] })
77
86
  .output(z.array(GroupSchema)),
78
87
 
79
88
  // ==========================================================================
80
- // SYSTEM MANAGEMENT (userType: "authenticated" with manage permission)
89
+ // SYSTEM MANAGEMENT (userType: "authenticated" with manage access)
81
90
  // ==========================================================================
82
91
 
83
92
  createSystem: _base
84
93
  .meta({
85
94
  userType: "authenticated",
86
- permissions: [permissions.catalogManage.id],
95
+ access: [catalogAccess.system.manage],
87
96
  })
88
97
  .input(CreateSystemInputSchema)
89
98
  .output(SystemSchema),
@@ -91,7 +100,7 @@ export const catalogContract = {
91
100
  updateSystem: _base
92
101
  .meta({
93
102
  userType: "authenticated",
94
- permissions: [permissions.catalogManage.id],
103
+ access: [catalogAccess.system.manage],
95
104
  })
96
105
  .input(UpdateSystemInputSchema)
97
106
  .output(SystemSchema),
@@ -99,19 +108,19 @@ export const catalogContract = {
99
108
  deleteSystem: _base
100
109
  .meta({
101
110
  userType: "authenticated",
102
- permissions: [permissions.catalogManage.id],
111
+ access: [catalogAccess.system.manage],
103
112
  })
104
113
  .input(z.string())
105
114
  .output(z.object({ success: z.boolean() })),
106
115
 
107
116
  // ==========================================================================
108
- // GROUP MANAGEMENT (userType: "authenticated" with manage permission)
117
+ // GROUP MANAGEMENT (userType: "authenticated" with manage access)
109
118
  // ==========================================================================
110
119
 
111
120
  createGroup: _base
112
121
  .meta({
113
122
  userType: "authenticated",
114
- permissions: [permissions.catalogManage.id],
123
+ access: [catalogAccess.group.manage],
115
124
  })
116
125
  .input(CreateGroupInputSchema)
117
126
  .output(GroupSchema),
@@ -119,7 +128,7 @@ export const catalogContract = {
119
128
  updateGroup: _base
120
129
  .meta({
121
130
  userType: "authenticated",
122
- permissions: [permissions.catalogManage.id],
131
+ access: [catalogAccess.group.manage],
123
132
  })
124
133
  .input(UpdateGroupInputSchema)
125
134
  .output(GroupSchema),
@@ -127,19 +136,19 @@ export const catalogContract = {
127
136
  deleteGroup: _base
128
137
  .meta({
129
138
  userType: "authenticated",
130
- permissions: [permissions.catalogManage.id],
139
+ access: [catalogAccess.group.manage],
131
140
  })
132
141
  .input(z.string())
133
142
  .output(z.object({ success: z.boolean() })),
134
143
 
135
144
  // ==========================================================================
136
- // SYSTEM-GROUP RELATIONSHIPS (userType: "authenticated" with manage permission)
145
+ // SYSTEM-GROUP RELATIONSHIPS (userType: "authenticated" with manage access)
137
146
  // ==========================================================================
138
147
 
139
148
  addSystemToGroup: _base
140
149
  .meta({
141
150
  userType: "authenticated",
142
- permissions: [permissions.catalogManage.id],
151
+ access: [catalogAccess.system.manage],
143
152
  })
144
153
  .input(
145
154
  z.object({
@@ -152,7 +161,7 @@ export const catalogContract = {
152
161
  removeSystemFromGroup: _base
153
162
  .meta({
154
163
  userType: "authenticated",
155
- permissions: [permissions.catalogManage.id],
164
+ access: [catalogAccess.system.manage],
156
165
  })
157
166
  .input(
158
167
  z.object({
@@ -167,11 +176,11 @@ export const catalogContract = {
167
176
  // ==========================================================================
168
177
 
169
178
  getViews: _base
170
- .meta({ userType: "user", permissions: [permissions.catalogRead.id] })
179
+ .meta({ userType: "user", access: [catalogAccess.view.read] })
171
180
  .output(z.array(ViewSchema)),
172
181
 
173
182
  createView: _base
174
- .meta({ userType: "user", permissions: [permissions.catalogManage.id] })
183
+ .meta({ userType: "user", access: [catalogAccess.view.manage] })
175
184
  .input(CreateViewInputSchema)
176
185
  .output(ViewSchema),
177
186
 
@@ -1,17 +0,0 @@
1
- import { createPermission } from "@checkstack/common";
2
-
3
- export const permissions = {
4
- catalogRead: createPermission(
5
- "catalog",
6
- "read",
7
- "Read Catalog (Systems and Groups)",
8
- { isAuthenticatedDefault: true, isPublicDefault: true }
9
- ),
10
- catalogManage: createPermission(
11
- "catalog",
12
- "manage",
13
- "Full management of Catalog (Systems and Groups)"
14
- ),
15
- };
16
-
17
- export const permissionList = Object.values(permissions);