kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add basic api acl
rodzic
30d909c6f1
commit
d02e70ea74
|
@ -2,6 +2,7 @@ import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
import type { AuthenticatedEnv } from '@/lib/types'
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
import { db, eq, schema, userIdSchema } from '@/db'
|
import { db, eq, schema, userIdSchema } from '@/db'
|
||||||
|
import { acl } from '@/lib/acl'
|
||||||
import { assert, parseZodSchema } from '@/lib/utils'
|
import { assert, parseZodSchema } from '@/lib/utils'
|
||||||
|
|
||||||
const ParamsSchema = z.object({
|
const ParamsSchema = z.object({
|
||||||
|
@ -43,7 +44,8 @@ export function registerV1UsersGetUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
||||||
const user = await db.query.users.findFirst({
|
const user = await db.query.users.findFirst({
|
||||||
where: eq(schema.users.id, userId)
|
where: eq(schema.users.id, userId)
|
||||||
})
|
})
|
||||||
assert(user, 404, `User not found: ${userId}`)
|
assert(user, 404, `User not found "${userId}"`)
|
||||||
|
acl(c, user, { label: 'User', userField: 'id' })
|
||||||
|
|
||||||
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
import type { AuthenticatedEnv } from '@/lib/types'
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
import { db, eq, schema, userIdSchema } from '@/db'
|
import { db, eq, schema, userIdSchema } from '@/db'
|
||||||
|
import { acl } from '@/lib/acl'
|
||||||
import { assert, parseZodSchema } from '@/lib/utils'
|
import { assert, parseZodSchema } from '@/lib/utils'
|
||||||
|
|
||||||
const ParamsSchema = z.object({
|
const ParamsSchema = z.object({
|
||||||
|
@ -49,12 +50,13 @@ export function registerV1UsersUpdateUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
||||||
const { userId } = c.req.valid('param')
|
const { userId } = c.req.valid('param')
|
||||||
const body = c.req.valid('json')
|
const body = c.req.valid('json')
|
||||||
|
|
||||||
const user = await db
|
const [user] = await db
|
||||||
.update(schema.users)
|
.update(schema.users)
|
||||||
.set(body)
|
.set(body)
|
||||||
.where(eq(schema.users.id, userId))
|
.where(eq(schema.users.id, userId))
|
||||||
.returning()
|
.returning()
|
||||||
assert(user, 404, `User not found: ${userId}`)
|
assert(user, 404, `User not found "${userId}"`)
|
||||||
|
acl(c, user, { label: 'User', userField: 'id' })
|
||||||
|
|
||||||
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import type { AuthenticatedContext } from './types'
|
||||||
|
import { assert } from './utils'
|
||||||
|
|
||||||
|
export function acl<
|
||||||
|
TModel extends Record<string, unknown> & { id: string },
|
||||||
|
TUserField extends keyof TModel = 'user',
|
||||||
|
TTeamField extends keyof TModel = 'team'
|
||||||
|
>(
|
||||||
|
ctx: AuthenticatedContext,
|
||||||
|
model: TModel,
|
||||||
|
{
|
||||||
|
label,
|
||||||
|
userField = 'user' as TUserField,
|
||||||
|
teamField = 'team' as TTeamField
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
userField?: TUserField
|
||||||
|
teamField?: TTeamField
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const user = ctx.get('user')
|
||||||
|
assert(user, 401, 'Authentication required')
|
||||||
|
|
||||||
|
const teamMember = ctx.get('teamMember')
|
||||||
|
|
||||||
|
const userFieldValue = model[userField]
|
||||||
|
const teamFieldValue = model[teamField]
|
||||||
|
|
||||||
|
const isAuthUserOwner = userFieldValue && userFieldValue === user.id
|
||||||
|
const isAuthUserAdmin = user.role === 'admin'
|
||||||
|
const hasTeamAccess =
|
||||||
|
teamMember && teamFieldValue && teamFieldValue === teamMember.teamId
|
||||||
|
|
||||||
|
assert(
|
||||||
|
isAuthUserOwner || isAuthUserAdmin || hasTeamAccess,
|
||||||
|
403,
|
||||||
|
`User does not have access to ${label} "${model.id}"`
|
||||||
|
)
|
||||||
|
}
|
Ładowanie…
Reference in New Issue