Skip to content

Commit aa506dd

Browse files
committed
fix: user patch sql error
1 parent 618b8dc commit aa506dd

File tree

3 files changed

+32
-26
lines changed

3 files changed

+32
-26
lines changed

scripts/test-sqlite.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ if [ $# -eq 0 ]; then
2020
vitest run --exclude="test/unit/"
2121
else
2222
vitest run "$@"
23-
fi
23+
fi

src/runtime/server/utils/user.ts

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -127,41 +127,27 @@ export const updateUser = async (id: number, userData: Partial<User>, options: M
127127
const db = await useDb(options)
128128
const usersTable = options.tables.users
129129

130-
// Explicitly define which fields are allowed to be updated.
131-
// This prevents mass-assignment vulnerabilities.
132130
const allowedFields: (keyof User)[] = ['name', 'email', 'role', 'active']
133-
const updates: string[] = []
134-
const values: (string | number | boolean)[] = []
135-
136-
for (const field of allowedFields) {
137-
if (userData[field] !== undefined) {
138-
updates.push(`${field} = ?`)
139-
values.push(userData[field])
140-
}
141-
}
142-
143-
// If the user is being deactivated, revoke their tokens
144-
if (userData.active === false) {
145-
await revokeUserTokens(id, options)
146-
}
131+
const fieldsToUpdate = allowedFields.filter(field => userData[field] !== undefined)
147132

148133
// If no valid fields are provided, there's nothing to update.
149-
if (updates.length === 0) {
134+
if (fieldsToUpdate.length === 0) {
150135
const currentUser = await findUserById(id, options)
151136
if (!currentUser) {
152137
throw new Error('User not found.')
153138
}
154139
return currentUser
155140
}
156141

157-
// Add the updated_at timestamp and the user ID for the WHERE clause
158-
updates.push('updated_at = CURRENT_TIMESTAMP')
159-
values.push(id)
160-
161-
// Use db.sql with template parts for dynamic column names
162-
const setClause = updates.map(update => update.replace(' = ?', '')).join(', ')
142+
// If the user is being deactivated, revoke their tokens
143+
if (userData.active === false) {
144+
await revokeUserTokens(id, options)
145+
}
163146

164-
await db.sql`UPDATE {${usersTable}} SET {${setClause}} WHERE id = ${id}`
147+
for (const field of fieldsToUpdate) {
148+
await db.sql`UPDATE {${usersTable}} SET {${field}} = ${userData[field]} WHERE id = ${id}`
149+
}
150+
await db.sql`UPDATE {${usersTable}} SET updated_at = CURRENT_TIMESTAMP WHERE id = ${id}`
165151

166152
const updatedUser = await findUserById(id, options)
167153

test/utils.user.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { DatabaseType, DatabaseConfig, ModuleOptions } from '../src/types'
44
import { cleanupTestSetup, createTestSetup } from './test-setup'
55
import { createUsersTable } from '../src/runtime/server/utils/create-users-table'
66
import { createPersonalAccessTokensTable } from '../src/runtime/server/utils/create-personal-access-tokens-table'
7-
import { createUser, findUserByEmail, updateUserPassword, getLastLoginTime, getCurrentUserFromToken } from '../src/runtime/server/utils/user'
7+
import { createUser, findUserByEmail, updateUser, updateUserPassword, getLastLoginTime, getCurrentUserFromToken } from '../src/runtime/server/utils/user'
88
import { addActiveToUsers } from '../src/runtime/server/utils/add-active-to-users'
99

1010
describe('User Utilities (src/utils/user.ts)', () => {
@@ -161,6 +161,26 @@ describe('User Utilities (src/utils/user.ts)', () => {
161161
})
162162
})
163163

164+
describe('updateUser', () => {
165+
it('should update user details in the database', async () => {
166+
// 1. Create a user
167+
const userData = { email: 'update@example.com', name: 'Original Name', password: 'password123' }
168+
const createdUser = await createUser(userData, testOptions)
169+
170+
// 2. Update the user's name and role
171+
const updates = { name: 'Updated Name', role: 'admin' }
172+
await updateUser(createdUser.id, updates, testOptions)
173+
174+
// 3. Fetch the user directly from the database to verify the update
175+
const result = await db.sql`SELECT * FROM {${testOptions.tables.users}} WHERE id = ${createdUser.id}`
176+
const dbUser = result.rows![0]
177+
178+
// 4. Assert that the details are updated
179+
expect(dbUser.name).toBe(updates.name)
180+
expect(dbUser.role).toBe(updates.role)
181+
})
182+
})
183+
164184
describe('Integration tests', () => {
165185
it('should work together: create user, find user, update password', async () => {
166186
const userData = { email: 'integration@webmania.cc', name: 'Integration User', password: 'initialpassword' }

0 commit comments

Comments
 (0)