parent
b080d319af
commit
c5015bf43f
6 changed files with 228 additions and 5 deletions
@ -0,0 +1,64 @@ |
|||||||
|
import assert from 'assert'; |
||||||
|
import { RequestHandler } from 'express'; |
||||||
|
|
||||||
|
import { REP_PEACEFUL_STRING, REP_UUID } from '../../consts'; |
||||||
|
|
||||||
|
import { insertOrUpdateUser, query } from '../../accessModule'; |
||||||
|
import { sanitize } from '../../sanitize'; |
||||||
|
import { openssl, stderr, stdoutVar } from '../../shell'; |
||||||
|
|
||||||
|
export const createUser: RequestHandler< |
||||||
|
unknown, |
||||||
|
CreateUserResponseBody, |
||||||
|
CreateUserRequestBody |
||||||
|
> = async (request, response) => { |
||||||
|
const { body: { password: rPassword, userName: rUserName } = {} } = request; |
||||||
|
|
||||||
|
const password = sanitize(rPassword, 'string', { |
||||||
|
fallback: openssl('rand', '-base64', '12').trim(), |
||||||
|
}); |
||||||
|
const userName = sanitize(rUserName, 'string', { modifierType: 'sql' }); |
||||||
|
|
||||||
|
stdoutVar({ password, userName }, 'Create user with params: '); |
||||||
|
|
||||||
|
try { |
||||||
|
assert( |
||||||
|
REP_PEACEFUL_STRING.test(password), |
||||||
|
`Password must be a peaceful string; got: [${password}]`, |
||||||
|
); |
||||||
|
|
||||||
|
assert( |
||||||
|
REP_PEACEFUL_STRING.test(userName), |
||||||
|
`User name must be a peaceful string; got: [${userName}]`, |
||||||
|
); |
||||||
|
|
||||||
|
const [[userCount]]: [[number]] = await query( |
||||||
|
`SELECT COUNT(user_uuid) FROM users WHERE user_name = '${userName}';`, |
||||||
|
); |
||||||
|
|
||||||
|
assert(userCount === 0, `User name [${userName}] already used`); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to assert value when creating user; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(400).send(); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const result = await insertOrUpdateUser({ |
||||||
|
file: __filename, |
||||||
|
user_name: userName, |
||||||
|
user_password_hash: password, |
||||||
|
}); |
||||||
|
|
||||||
|
assert( |
||||||
|
REP_UUID.test(result), |
||||||
|
`Insert or update failed with result [${result}]`, |
||||||
|
); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to record user to database; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
|
||||||
|
return response.status(201).send({ password }); |
||||||
|
}; |
@ -1,2 +1,4 @@ |
|||||||
|
export * from './createUser'; |
||||||
export * from './deleteUser'; |
export * from './deleteUser'; |
||||||
export * from './getUser'; |
export * from './getUser'; |
||||||
|
export * from './updateUser'; |
||||||
|
@ -0,0 +1,136 @@ |
|||||||
|
import assert from 'assert'; |
||||||
|
import { RequestHandler } from 'express'; |
||||||
|
|
||||||
|
import { REP_PEACEFUL_STRING, REP_UUID } from '../../consts'; |
||||||
|
|
||||||
|
import { encrypt, query, write } from '../../accessModule'; |
||||||
|
import { sanitize } from '../../sanitize'; |
||||||
|
import { stderr, stdoutVar } from '../../shell'; |
||||||
|
|
||||||
|
export const updateUser: RequestHandler< |
||||||
|
UserParamsDictionary, |
||||||
|
undefined, |
||||||
|
UpdateUserRequestBody |
||||||
|
> = async (request, response) => { |
||||||
|
const { |
||||||
|
body: { password: rPassword, userName: rUserName } = {}, |
||||||
|
params: { userUuid }, |
||||||
|
} = request; |
||||||
|
|
||||||
|
const password = sanitize(rPassword, 'string'); |
||||||
|
const userName = sanitize(rUserName, 'string', { modifierType: 'sql' }); |
||||||
|
|
||||||
|
stdoutVar({ password, userName }, `Update user ${userUuid} with params: `); |
||||||
|
|
||||||
|
try { |
||||||
|
if (password.length) { |
||||||
|
assert( |
||||||
|
REP_PEACEFUL_STRING.test(password), |
||||||
|
`Password must be a valid peaceful string; got: [${password}]`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (userName.length) { |
||||||
|
assert( |
||||||
|
REP_PEACEFUL_STRING.test(userName), |
||||||
|
`User name must be a peaceful string; got: [${userName}]`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
assert( |
||||||
|
REP_UUID.test(userUuid), |
||||||
|
`User UUID must be a valid UUIDv4; got: [${userUuid}]`, |
||||||
|
); |
||||||
|
|
||||||
|
const [[existingUserName]]: [[string]] = await query( |
||||||
|
`SELECT user_name FROM users WHERE user_uuid = '${userUuid}';`, |
||||||
|
); |
||||||
|
|
||||||
|
assert(existingUserName !== 'admin' || userName, 'Cannot '); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Assert failed when update user; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(400).send(); |
||||||
|
} |
||||||
|
|
||||||
|
let existingUser: [ |
||||||
|
[ |
||||||
|
user_name: string, |
||||||
|
user_password_hash: string, |
||||||
|
user_salt: string, |
||||||
|
user_algorithm: string, |
||||||
|
user_hash_count: string, |
||||||
|
], |
||||||
|
]; |
||||||
|
|
||||||
|
try { |
||||||
|
existingUser = await query( |
||||||
|
`SELECT
|
||||||
|
user_name, |
||||||
|
user_password_hash, |
||||||
|
user_salt, |
||||||
|
user_algorithm, |
||||||
|
user_hash_count |
||||||
|
FROM users |
||||||
|
WHERE user_uuid = '${userUuid}' |
||||||
|
ORDER BY modified_date DESC |
||||||
|
LIMIT 1;`,
|
||||||
|
); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to find existing user ${userUuid}; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
|
||||||
|
if (existingUser.length !== 1) { |
||||||
|
return response.status(404).send(); |
||||||
|
} |
||||||
|
|
||||||
|
const [[xUserName, xPasswordHash, xSalt, xAlgorithm, xHashCount]] = |
||||||
|
existingUser; |
||||||
|
|
||||||
|
const assigns: string[] = []; |
||||||
|
|
||||||
|
if (password.length) { |
||||||
|
let passwordHash: string; |
||||||
|
|
||||||
|
try { |
||||||
|
({ user_password_hash: passwordHash } = await encrypt({ |
||||||
|
algorithm: xAlgorithm, |
||||||
|
hash_count: xHashCount, |
||||||
|
password, |
||||||
|
salt: xSalt, |
||||||
|
})); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Encrypt failed when update user; CAUSE ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
|
||||||
|
if (passwordHash !== xPasswordHash) { |
||||||
|
assigns.push(`user_password_hash = '${passwordHash}'`); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (userName.length && userName !== xUserName) { |
||||||
|
assigns.push(`user_name = '${userName}'`); |
||||||
|
} |
||||||
|
|
||||||
|
if (assigns.length) { |
||||||
|
try { |
||||||
|
const wcode = await write( |
||||||
|
`UPDATE users SET ${assigns.join( |
||||||
|
',', |
||||||
|
)} WHERE user_uuid = '${userUuid}';`,
|
||||||
|
); |
||||||
|
|
||||||
|
assert(wcode === 0, `Update users failed with code: ${wcode}`); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to record user changes to database; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return response.send(); |
||||||
|
}; |
@ -1,7 +1,18 @@ |
|||||||
type DeleteUserParamsDictionary = { |
type CreateUserRequestBody = { |
||||||
userUuid: string; |
password?: string; |
||||||
|
userName: string; |
||||||
|
}; |
||||||
|
|
||||||
|
type CreateUserResponseBody = { |
||||||
|
password: string; |
||||||
}; |
}; |
||||||
|
|
||||||
type DeleteUserRequestBody = { |
type DeleteUserRequestBody = { |
||||||
uuids?: string[]; |
uuids?: string[]; |
||||||
}; |
}; |
||||||
|
|
||||||
|
type UpdateUserRequestBody = Partial<CreateUserRequestBody>; |
||||||
|
|
||||||
|
type UserParamsDictionary = { |
||||||
|
userUuid: string; |
||||||
|
}; |
||||||
|
Loading…
Reference in new issue