@checkstack/auth-backend 0.0.3 → 0.2.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/drizzle/0002_lowly_squirrel_girl.sql +43 -0
- package/drizzle/0003_tranquil_sally_floyd.sql +8 -0
- package/drizzle/0004_lucky_power_man.sql +21 -0
- package/drizzle/meta/0002_snapshot.json +1017 -0
- package/drizzle/meta/0003_snapshot.json +1050 -0
- package/drizzle/meta/0004_snapshot.json +1050 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +1 -1
- package/src/index.ts +176 -162
- package/src/router.test.ts +11 -11
- package/src/router.ts +525 -90
- package/src/schema.ts +125 -18
- package/src/teams.test.ts +1985 -0
- package/src/utils/user.test.ts +65 -46
- package/src/utils/user.ts +21 -13
package/src/schema.ts
CHANGED
|
@@ -66,23 +66,23 @@ export const role = pgTable("role", {
|
|
|
66
66
|
isSystem: boolean("is_system").default(false), // Prevent deletion of core roles
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
export const
|
|
69
|
+
export const accessRule = pgTable("access_rule", {
|
|
70
70
|
id: text("id").primaryKey(), // 'core.manage-users', etc.
|
|
71
71
|
description: text("description"),
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
export const
|
|
75
|
-
"
|
|
74
|
+
export const roleAccessRule = pgTable(
|
|
75
|
+
"role_access_rule",
|
|
76
76
|
{
|
|
77
77
|
roleId: text("role_id")
|
|
78
78
|
.notNull()
|
|
79
79
|
.references(() => role.id),
|
|
80
|
-
|
|
80
|
+
accessRuleId: text("access_rule_id")
|
|
81
81
|
.notNull()
|
|
82
|
-
.references(() =>
|
|
82
|
+
.references(() => accessRule.id),
|
|
83
83
|
},
|
|
84
84
|
(t) => ({
|
|
85
|
-
pk: primaryKey({ columns: [t.roleId, t.
|
|
85
|
+
pk: primaryKey({ columns: [t.roleId, t.accessRuleId] }),
|
|
86
86
|
})
|
|
87
87
|
);
|
|
88
88
|
|
|
@@ -102,31 +102,31 @@ export const userRole = pgTable(
|
|
|
102
102
|
);
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Tracks authenticated default
|
|
106
|
-
* When a plugin registers
|
|
105
|
+
* Tracks authenticated default access rules that have been disabled by admins.
|
|
106
|
+
* When a plugin registers an access rule with isAuthenticatedDefault=true, it gets assigned
|
|
107
107
|
* to the "users" role unless it's in this table.
|
|
108
108
|
*/
|
|
109
|
-
export const
|
|
110
|
-
"
|
|
109
|
+
export const disabledDefaultAccessRule = pgTable(
|
|
110
|
+
"disabled_default_access_rule",
|
|
111
111
|
{
|
|
112
|
-
|
|
112
|
+
accessRuleId: text("access_rule_id")
|
|
113
113
|
.primaryKey()
|
|
114
|
-
.references(() =>
|
|
114
|
+
.references(() => accessRule.id),
|
|
115
115
|
disabledAt: timestamp("disabled_at").notNull(),
|
|
116
116
|
}
|
|
117
117
|
);
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
* Tracks public default
|
|
121
|
-
* When a plugin registers
|
|
120
|
+
* Tracks public default access rules that have been disabled by admins.
|
|
121
|
+
* When a plugin registers an access rule with isPublicDefault=true, it gets assigned
|
|
122
122
|
* to the "anonymous" role unless it's in this table.
|
|
123
123
|
*/
|
|
124
|
-
export const
|
|
125
|
-
"
|
|
124
|
+
export const disabledPublicDefaultAccessRule = pgTable(
|
|
125
|
+
"disabled_public_default_access_rule",
|
|
126
126
|
{
|
|
127
|
-
|
|
127
|
+
accessRuleId: text("access_rule_id")
|
|
128
128
|
.primaryKey()
|
|
129
|
-
.references(() =>
|
|
129
|
+
.references(() => accessRule.id),
|
|
130
130
|
disabledAt: timestamp("disabled_at").notNull(),
|
|
131
131
|
}
|
|
132
132
|
);
|
|
@@ -171,3 +171,110 @@ export const applicationRole = pgTable(
|
|
|
171
171
|
pk: primaryKey({ columns: [t.applicationId, t.roleId] }),
|
|
172
172
|
})
|
|
173
173
|
);
|
|
174
|
+
|
|
175
|
+
// --- Teams Schema ---
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Teams for resource-level access control.
|
|
179
|
+
* Users can be members of multiple teams, and resources can be scoped to teams.
|
|
180
|
+
*/
|
|
181
|
+
export const team = pgTable("team", {
|
|
182
|
+
id: text("id").primaryKey(),
|
|
183
|
+
name: text("name").notNull(),
|
|
184
|
+
description: text("description"),
|
|
185
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
186
|
+
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* User-to-Team membership (M:N).
|
|
191
|
+
* Users can belong to multiple teams.
|
|
192
|
+
*/
|
|
193
|
+
export const userTeam = pgTable(
|
|
194
|
+
"user_team",
|
|
195
|
+
{
|
|
196
|
+
userId: text("user_id")
|
|
197
|
+
.notNull()
|
|
198
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
199
|
+
teamId: text("team_id")
|
|
200
|
+
.notNull()
|
|
201
|
+
.references(() => team.id, { onDelete: "cascade" }),
|
|
202
|
+
},
|
|
203
|
+
(t) => ({
|
|
204
|
+
pk: primaryKey({ columns: [t.userId, t.teamId] }),
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Application-to-Team membership (M:N).
|
|
210
|
+
* API keys can belong to teams for resource access.
|
|
211
|
+
*/
|
|
212
|
+
export const applicationTeam = pgTable(
|
|
213
|
+
"application_team",
|
|
214
|
+
{
|
|
215
|
+
applicationId: text("application_id")
|
|
216
|
+
.notNull()
|
|
217
|
+
.references(() => application.id, { onDelete: "cascade" }),
|
|
218
|
+
teamId: text("team_id")
|
|
219
|
+
.notNull()
|
|
220
|
+
.references(() => team.id, { onDelete: "cascade" }),
|
|
221
|
+
},
|
|
222
|
+
(t) => ({
|
|
223
|
+
pk: primaryKey({ columns: [t.applicationId, t.teamId] }),
|
|
224
|
+
})
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Team managers - users who can manage a specific team's membership and resource access.
|
|
229
|
+
* Team managers cannot delete the team or manage other teams.
|
|
230
|
+
*/
|
|
231
|
+
export const teamManager = pgTable(
|
|
232
|
+
"team_manager",
|
|
233
|
+
{
|
|
234
|
+
teamId: text("team_id")
|
|
235
|
+
.notNull()
|
|
236
|
+
.references(() => team.id, { onDelete: "cascade" }),
|
|
237
|
+
userId: text("user_id")
|
|
238
|
+
.notNull()
|
|
239
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
240
|
+
},
|
|
241
|
+
(t) => ({
|
|
242
|
+
pk: primaryKey({ columns: [t.teamId, t.userId] }),
|
|
243
|
+
})
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Resource-level access settings.
|
|
248
|
+
* Controls whether a resource requires team membership (teamOnly) vs allowing global access.
|
|
249
|
+
*/
|
|
250
|
+
export const resourceAccessSettings = pgTable(
|
|
251
|
+
"resource_access_settings",
|
|
252
|
+
{
|
|
253
|
+
resourceType: text("resource_type").notNull(), // e.g., "catalog.system"
|
|
254
|
+
resourceId: text("resource_id").notNull(),
|
|
255
|
+
teamOnly: boolean("team_only").notNull().default(false), // If true, global access doesn't apply
|
|
256
|
+
},
|
|
257
|
+
(t) => ({
|
|
258
|
+
pk: primaryKey({ columns: [t.resourceType, t.resourceId] }),
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Centralized resource-level access control.
|
|
264
|
+
* Stores team grants for all resource types across the platform.
|
|
265
|
+
*/
|
|
266
|
+
export const resourceTeamAccess = pgTable(
|
|
267
|
+
"resource_team_access",
|
|
268
|
+
{
|
|
269
|
+
resourceType: text("resource_type").notNull(), // e.g., "catalog.system"
|
|
270
|
+
resourceId: text("resource_id").notNull(),
|
|
271
|
+
teamId: text("team_id")
|
|
272
|
+
.notNull()
|
|
273
|
+
.references(() => team.id, { onDelete: "cascade" }),
|
|
274
|
+
canRead: boolean("can_read").notNull().default(true),
|
|
275
|
+
canManage: boolean("can_manage").notNull().default(false),
|
|
276
|
+
},
|
|
277
|
+
(t) => ({
|
|
278
|
+
pk: primaryKey({ columns: [t.resourceType, t.resourceId, t.teamId] }),
|
|
279
|
+
})
|
|
280
|
+
);
|