mirror of https://github.com/wg-easy/wg-easy
11 changed files with 213 additions and 12 deletions
@ -0,0 +1,15 @@ |
|||||
|
import { sql } from 'drizzle-orm'; |
||||
|
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; |
||||
|
|
||||
|
export const sessionConfig = sqliteTable('session_config_table', { |
||||
|
// limit to one entry
|
||||
|
id: int().primaryKey({ autoIncrement: false }).default(1), |
||||
|
password: text().notNull(), |
||||
|
createdAt: text('created_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`), |
||||
|
updatedAt: text('updated_at') |
||||
|
.notNull() |
||||
|
.default(sql`(CURRENT_TIMESTAMP)`) |
||||
|
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), |
||||
|
}); |
@ -0,0 +1,26 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
find: db.query.sessionConfig.findFirst().prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class SessionConfigService { |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @throws |
||||
|
*/ |
||||
|
async get() { |
||||
|
const result = await this.#statements.find.all(); |
||||
|
if (!result) { |
||||
|
throw new Error('Session Config not found'); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
import type { DBType } from '#db/sqlite'; |
||||
|
import { eq, sql } from 'drizzle-orm'; |
||||
|
import { user } from './schema'; |
||||
|
import type { ID } from '../../schema'; |
||||
|
|
||||
|
function createPreparedStatement(db: DBType) { |
||||
|
return { |
||||
|
findAll: db.query.user.findMany().prepare(), |
||||
|
findById: db.query.user |
||||
|
.findFirst({ where: eq(user.id, sql.placeholder('id')) }) |
||||
|
.prepare(), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export class UserService { |
||||
|
#statements: ReturnType<typeof createPreparedStatement>; |
||||
|
|
||||
|
constructor(db: DBType) { |
||||
|
this.#statements = createPreparedStatement(db); |
||||
|
} |
||||
|
|
||||
|
async getAll() { |
||||
|
return this.#statements.findAll.all(); |
||||
|
} |
||||
|
|
||||
|
async get(id: ID) { |
||||
|
return this.#statements.findById.all({ id }); |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
import type { InferSelectModel } from 'drizzle-orm'; |
||||
|
import type { user } from './schema'; |
||||
|
|
||||
|
export type UserType = InferSelectModel<typeof user>; |
@ -0,0 +1,101 @@ |
|||||
|
import type { |
||||
|
EventHandler, |
||||
|
EventHandlerRequest, |
||||
|
EventHandlerResponse, |
||||
|
H3Event, |
||||
|
} from 'h3'; |
||||
|
import type { UserType } from '#db/repositories/user/types'; |
||||
|
|
||||
|
export const definePermissionEventHandler = < |
||||
|
TReq extends EventHandlerRequest, |
||||
|
TRes extends EventHandlerResponse, |
||||
|
>( |
||||
|
handler: EventHandler<TReq, TRes> |
||||
|
) => { |
||||
|
return defineEventHandler(async (event) => { |
||||
|
const user = await getCurrentUser(event); |
||||
|
checkPermissions('', user); |
||||
|
|
||||
|
return await handler(event); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
function checkPermissions(permission: unknown, user: UserType) { |
||||
|
console.log({ permission, user }); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @throws |
||||
|
*/ |
||||
|
async function getCurrentUser(event: H3Event) { |
||||
|
const session = await getWGSession(event); |
||||
|
|
||||
|
const authorization = getHeader(event, 'Authorization'); |
||||
|
|
||||
|
let user: UserType | undefined = undefined; |
||||
|
if (session.data.userId) { |
||||
|
// Handle if authenticating using Session
|
||||
|
user = await Database.users.get(session.data.userId); |
||||
|
} else if (authorization) { |
||||
|
// Handle if authenticating using Header
|
||||
|
const [method, value] = authorization.split(' '); |
||||
|
// Support Basic Authentication
|
||||
|
// TODO: support personal access token or similar
|
||||
|
if (method !== 'Basic' || !value) { |
||||
|
throw createError({ |
||||
|
statusCode: 401, |
||||
|
statusMessage: 'Session failed', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const basicValue = Buffer.from(value, 'base64').toString('utf-8'); |
||||
|
|
||||
|
// Split by first ":"
|
||||
|
const index = basicValue.indexOf(':'); |
||||
|
const userId = basicValue.substring(0, index); |
||||
|
const password = basicValue.substring(index + 1); |
||||
|
|
||||
|
if (!userId || !password) { |
||||
|
throw createError({ |
||||
|
statusCode: 401, |
||||
|
statusMessage: 'Session failed', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const foundUser = await Database.users.get(Number.parseInt(userId)); |
||||
|
|
||||
|
if (!foundUser) { |
||||
|
throw createError({ |
||||
|
statusCode: 401, |
||||
|
statusMessage: 'Session failed', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const userHashPassword = foundUser.password; |
||||
|
const passwordValid = await isPasswordValid(password, userHashPassword); |
||||
|
|
||||
|
if (!passwordValid) { |
||||
|
throw createError({ |
||||
|
statusCode: 401, |
||||
|
statusMessage: 'Incorrect Password', |
||||
|
}); |
||||
|
} |
||||
|
user = foundUser; |
||||
|
} |
||||
|
|
||||
|
if (!user) { |
||||
|
throw createError({ |
||||
|
statusCode: 401, |
||||
|
statusMessage: 'Session failed. User not found', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (!user.enabled) { |
||||
|
throw createError({ |
||||
|
statusCode: 403, |
||||
|
statusMessage: 'User is disabled', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return user; |
||||
|
} |
@ -1,10 +1,26 @@ |
|||||
import type { H3Event } from 'h3'; |
import type { H3Event } from 'h3'; |
||||
|
import type { ID } from '#db/schema'; |
||||
|
|
||||
export type WGSession = Partial<{ |
export type WGSession = Partial<{ |
||||
userId: string; |
userId: ID; |
||||
}>; |
}>; |
||||
|
|
||||
|
const name = 'wg-easy'; |
||||
|
|
||||
export async function useWGSession(event: H3Event) { |
export async function useWGSession(event: H3Event) { |
||||
const system = await Database.system.get(); |
const sessionConfig = await Database.sessionConfig.get(); |
||||
return useSession<WGSession>(event, system.sessionConfig); |
return useSession<WGSession>(event, { |
||||
|
password: sessionConfig.password, |
||||
|
name, |
||||
|
cookie: {}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export async function getWGSession(event: H3Event) { |
||||
|
const sessionConfig = await Database.sessionConfig.get(); |
||||
|
return getSession<WGSession>(event, { |
||||
|
password: sessionConfig.password, |
||||
|
name, |
||||
|
cookie: {}, |
||||
|
}); |
||||
} |
} |
||||
|
Loading…
Reference in new issue