@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 +174 -0
- package/package.json +1 -1
- package/src/access.ts +69 -0
- package/src/index.ts +1 -1
- package/src/rpc-contract.ts +29 -20
- package/src/permissions.ts +0 -17
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
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
package/src/rpc-contract.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
54
|
+
// ENTITY READ ENDPOINTS (userType: "public" - accessible by anyone with access)
|
|
55
55
|
// ==========================================================================
|
|
56
56
|
|
|
57
57
|
getEntities: _base
|
|
58
|
-
.meta({
|
|
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({
|
|
68
|
-
|
|
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({
|
|
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",
|
|
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
|
|
89
|
+
// SYSTEM MANAGEMENT (userType: "authenticated" with manage access)
|
|
81
90
|
// ==========================================================================
|
|
82
91
|
|
|
83
92
|
createSystem: _base
|
|
84
93
|
.meta({
|
|
85
94
|
userType: "authenticated",
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
117
|
+
// GROUP MANAGEMENT (userType: "authenticated" with manage access)
|
|
109
118
|
// ==========================================================================
|
|
110
119
|
|
|
111
120
|
createGroup: _base
|
|
112
121
|
.meta({
|
|
113
122
|
userType: "authenticated",
|
|
114
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
179
|
+
.meta({ userType: "user", access: [catalogAccess.view.read] })
|
|
171
180
|
.output(z.array(ViewSchema)),
|
|
172
181
|
|
|
173
182
|
createView: _base
|
|
174
|
-
.meta({ userType: "user",
|
|
183
|
+
.meta({ userType: "user", access: [catalogAccess.view.manage] })
|
|
175
184
|
.input(CreateViewInputSchema)
|
|
176
185
|
.output(ViewSchema),
|
|
177
186
|
|
package/src/permissions.ts
DELETED
|
@@ -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);
|