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 { db, eq, schema, userIdSchema } from '@/db'
|
||||
import { acl } from '@/lib/acl'
|
||||
import { assert, parseZodSchema } from '@/lib/utils'
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
|
@ -43,7 +44,8 @@ export function registerV1UsersGetUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
|||
const user = await db.query.users.findFirst({
|
||||
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))
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
|||
|
||||
import type { AuthenticatedEnv } from '@/lib/types'
|
||||
import { db, eq, schema, userIdSchema } from '@/db'
|
||||
import { acl } from '@/lib/acl'
|
||||
import { assert, parseZodSchema } from '@/lib/utils'
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
|
@ -49,12 +50,13 @@ export function registerV1UsersUpdateUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
|||
const { userId } = c.req.valid('param')
|
||||
const body = c.req.valid('json')
|
||||
|
||||
const user = await db
|
||||
const [user] = await db
|
||||
.update(schema.users)
|
||||
.set(body)
|
||||
.where(eq(schema.users.id, userId))
|
||||
.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))
|
||||
})
|
||||
|
|
|
@ -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