Skip to content

fix: regression of password hashing & fine tune default logging #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-monorepo",
"version": "0.2.12",
"version": "0.2.15",
"description": "",
"scripts": {
"build": "pnpm -r build",
Expand Down
6 changes: 4 additions & 2 deletions packages/internal/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/internal",
"version": "0.2.12",
"version": "0.2.15",
"displayName": "ZenStack Internal Library",
"description": "ZenStack internal runtime library. This package is for supporting runtime functionality of ZenStack and not supposed to be used directly.",
"repository": {
Expand All @@ -10,7 +10,8 @@
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "tsc",
"clean": "rimraf lib",
"build": "npm run clean && tsc",
"watch": "tsc --watch",
"lint": "eslint src --ext ts",
"prepublishOnly": "pnpm build"
Expand Down Expand Up @@ -45,6 +46,7 @@
"@types/uuid": "^8.3.4",
"eslint": "^8.27.0",
"jest": "^29.0.3",
"rimraf": "^3.0.2",
"ts-jest": "^29.0.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.7.0",
Expand Down
8 changes: 6 additions & 2 deletions packages/internal/src/handler/data/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export default class DataHandler<DbClient extends DbClientContract>
break;
}
} catch (err: unknown) {
this.service.error(`${method} ${model}: ${err}`);

if (err instanceof RequestHandlerError) {
this.service.warn(`${method} ${model}: ${err}`);

// in case of errors thrown directly by ZenStack
switch (err.code) {
case ServerErrorCode.DENIED_BY_POLICY:
Expand All @@ -105,6 +105,8 @@ export default class DataHandler<DbClient extends DbClientContract>
});
}
} else if (this.isPrismaClientKnownRequestError(err)) {
this.service.warn(`${method} ${model}: ${err}`);

// errors thrown by Prisma, try mapping to a known error
if (PRISMA_ERROR_MAPPING[err.code]) {
res.status(400).send({
Expand All @@ -120,6 +122,8 @@ export default class DataHandler<DbClient extends DbClientContract>
});
}
} else if (this.isPrismaClientValidationError(err)) {
this.service.warn(`${method} ${model}: ${err}`);

// prisma validation error
res.status(400).send({
code: ServerErrorCode.INVALID_REQUEST_PARAMS,
Expand Down
2 changes: 1 addition & 1 deletion packages/internal/src/handler/data/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ export async function preprocessWritePayload(
const pwdAttr = fieldInfo.attributes?.find(
(attr) => attr.name === '@password'
);
if (pwdAttr && fieldInfo.type !== 'String') {
if (pwdAttr && fieldInfo.type === 'String') {
// hash password value
let salt: string | number | undefined = pwdAttr.args.find(
(arg) => arg.name === 'salt'
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/runtime",
"displayName": "ZenStack Runtime Library",
"version": "0.2.12",
"version": "0.2.15",
"description": "This package contains runtime library for consuming client and server side code generated by ZenStack.",
"repository": {
"type": "git",
Expand Down
8 changes: 5 additions & 3 deletions packages/schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack Language Tools",
"description": "ZenStack is a toolkit that simplifies full-stack development",
"version": "0.2.12",
"version": "0.2.15",
"author": {
"name": "ZenStack Team"
},
Expand Down Expand Up @@ -67,8 +67,9 @@
"vscode:publish": "vsce publish --no-dependencies",
"vscode:prepublish": "cp ../../README.md ./ && pnpm lint && pnpm build",
"vscode:package": "vsce package --no-dependencies",
"clean": "rimraf bundle",
"build": "pnpm langium:generate && tsc --noEmit && pnpm bundle && cp -r src/res/* bundle/res/",
"bundle": "node build/bundle.js --minify",
"bundle": "npm run clean && node build/bundle.js --minify",
"bundle-watch": "node build/bundle.js --watch",
"ts:watch": "tsc --watch --noEmit",
"tsc-alias:watch": "tsc-alias --watch",
Expand All @@ -77,7 +78,7 @@
"langium:watch": "langium generate --watch",
"watch": "concurrently --kill-others \"npm:langium:watch\" \"npm:bundle-watch\"",
"test": "jest",
"prepublishOnly": "cp ../../README.md ./ && pnpm build && pnpm bundle"
"prepublishOnly": "cp ../../README.md ./ && pnpm build"
},
"dependencies": {
"@zenstackhq/internal": "workspace:*",
Expand Down Expand Up @@ -112,6 +113,7 @@
"eslint": "^8.27.0",
"jest": "^29.2.1",
"langium-cli": "^0.5.0",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
Expand Down
10 changes: 6 additions & 4 deletions packages/schema/src/res/stdlib.zmodel
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ attribute @@deny(_ operation: String, _ condition: Boolean)
* Indicates that the field is a password field and needs to be hashed before persistence.
*
* ZenStack uses `bcryptjs` library to hash password. You can use the `saltLength` parameter
* to configure length of salt, or use parameter to provide an explicit salt. By default, 12-byte
* long salt is used.
* to configure the cost of hashing, or use `salt` parameter to provide an explicit salt.
* By default, salt length of 12 is used.
*
* @saltLength: length of salt to use
* @salt: salt to use
* @see https://www.npmjs.com/package/bcryptjs for details
*
* @saltLength: length of salt to use (cost factor for the hash function)
* @salt: salt to use (a pregenerated valid salt)
*/
attribute @password(saltLength: Int?, salt: String?)

Expand Down
4 changes: 4 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion samples/todo/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "todo",
"version": "0.2.12",
"version": "0.2.15",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
3 changes: 3 additions & 0 deletions samples/todo/zenstack.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"log": ["info", "warn", "error"]
}
62 changes: 31 additions & 31 deletions tests/integration/tests/logging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ describe('Logging tests', () => {
let gotInfoEmit = false;
let gotQueryEmit = false;
let gotVerboseEmit = false;
let gotErrorEmit = false;
let gotWarnEmit = false;

let gotInfoStd = false;
let gotQueryStd = false;
let gotVerboseStd = false;
let gotErrorStd = false;
let gotWarnStd = false;

console.log = jest.fn((...args) => {
const msg = args?.[0] as string;
Expand All @@ -46,10 +46,10 @@ describe('Logging tests', () => {
}
});

console.error = jest.fn((...args) => {
console.warn = jest.fn((...args) => {
const msg = args?.[0] as string;
if (msg.includes(':error')) {
gotErrorStd = true;
if (msg.includes(':warn')) {
gotWarnStd = true;
}
});

Expand All @@ -68,9 +68,9 @@ describe('Logging tests', () => {
gotVerboseEmit = true;
});

service.$on('error', (event) => {
console.log('Got error', event);
gotErrorEmit = true;
service.$on('warn', (event) => {
console.log('Got warn', event);
gotWarnEmit = true;
});

await makeClient('/api/data/User').post('/').send({
Expand All @@ -80,20 +80,20 @@ describe('Logging tests', () => {
expect(gotQueryStd).toBeFalsy();
expect(gotVerboseStd).toBeFalsy();
expect(gotInfoStd).toBeFalsy();
expect(gotErrorStd).toBeTruthy();
expect(gotWarnStd).toBeTruthy();

expect(gotInfoEmit).toBeFalsy();
expect(gotQueryEmit).toBeFalsy();
expect(gotVerboseEmit).toBeFalsy();
expect(gotErrorEmit).toBeFalsy();
expect(gotWarnEmit).toBeFalsy();
});

it('logging with stdout', async () => {
fs.writeFileSync(
'./zenstack.config.json',
`
{
"log": ["query", "verbose", "info", "error"]
"log": ["query", "verbose", "info", "warn"]
}
`
);
Expand All @@ -104,12 +104,12 @@ describe('Logging tests', () => {
let gotInfoEmit = false;
let gotQueryEmit = false;
let gotVerboseEmit = false;
let gotErrorEmit = false;
let gotWarnEmit = false;

let gotInfoStd = false;
let gotQueryStd = false;
let gotVerboseStd = false;
let gotErrorStd = false;
let gotWarnStd = false;

console.log = jest.fn((...args) => {
const msg = args?.[0] as string;
Expand All @@ -124,10 +124,10 @@ describe('Logging tests', () => {
}
});

console.error = jest.fn((...args) => {
console.warn = jest.fn((...args) => {
const msg = args?.[0] as string;
if (msg.includes(':error')) {
gotErrorStd = true;
if (msg.includes(':warn')) {
gotWarnStd = true;
}
});

Expand All @@ -146,9 +146,9 @@ describe('Logging tests', () => {
gotVerboseEmit = true;
});

service.$on('error', (event) => {
console.log('Got error', event);
gotErrorEmit = true;
service.$on('warn', (event) => {
console.log('Got warn', event);
gotWarnEmit = true;
});

await makeClient('/api/data/User').post('/').send({
Expand All @@ -158,12 +158,12 @@ describe('Logging tests', () => {
expect(gotQueryStd).toBeTruthy();
expect(gotVerboseStd).toBeTruthy();
expect(gotInfoStd).toBeTruthy();
expect(gotErrorStd).toBeTruthy();
expect(gotWarnStd).toBeTruthy();

expect(gotInfoEmit).toBeFalsy();
expect(gotQueryEmit).toBeFalsy();
expect(gotVerboseEmit).toBeFalsy();
expect(gotErrorEmit).toBeFalsy();
expect(gotWarnEmit).toBeFalsy();
});

it('logging with event', async () => {
Expand All @@ -175,7 +175,7 @@ describe('Logging tests', () => {
{ "level": "query", "emit": "event" },
{ "level": "verbose", "emit": "event" },
{ "level": "info", "emit": "event" },
{ "level": "error", "emit": "event" }
{ "level": "warn", "emit": "event" }
]
}
`
Expand All @@ -187,12 +187,12 @@ describe('Logging tests', () => {
let gotInfoEmit = false;
let gotQueryEmit = false;
let gotVerboseEmit = false;
let gotErrorEmit = false;
let gotWarnEmit = false;

let gotInfoStd = false;
let gotQueryStd = false;
let gotVerboseStd = false;
let gotErrorStd = false;
let gotWarnStd = false;

console.log = jest.fn((...args) => {
const msg = args?.[0] as string;
Expand All @@ -207,10 +207,10 @@ describe('Logging tests', () => {
}
});

console.error = jest.fn((...args) => {
console.warn = jest.fn((...args) => {
const msg = args?.[0] as string;
if (msg.includes('zenstack:error')) {
gotErrorStd = true;
if (msg.includes('zenstack:warn')) {
gotWarnStd = true;
}
});

Expand All @@ -233,10 +233,10 @@ describe('Logging tests', () => {
gotVerboseEmit = true;
});

service.$on('error', (event) => {
service.$on('warn', (event) => {
expect(event.timestamp).not.toBeUndefined();
expect(event.message).not.toBeUndefined();
gotErrorEmit = true;
gotWarnEmit = true;
});

await makeClient('/api/data/User').post('/').send({
Expand All @@ -246,11 +246,11 @@ describe('Logging tests', () => {
expect(gotInfoEmit).toBeTruthy();
expect(gotQueryEmit).toBeTruthy();
expect(gotVerboseEmit).toBeTruthy();
expect(gotErrorEmit).toBeTruthy();
expect(gotWarnEmit).toBeTruthy();

expect(gotInfoStd).toBeFalsy();
expect(gotQueryStd).toBeFalsy();
expect(gotVerboseStd).toBeFalsy();
expect(gotErrorStd).toBeFalsy();
expect(gotWarnStd).toBeFalsy();
});
});
8 changes: 5 additions & 3 deletions tests/integration/tests/password.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('Password attribute tests', () => {
},
})
.expect(async (resp) =>
expect(compareSync('abc123', resp.body.password))
expect(compareSync('abc123', resp.body.password)).toBeTruthy()
);

await makeClient('/api/data/User/1')
Expand All @@ -39,7 +39,7 @@ describe('Password attribute tests', () => {
},
})
.expect(async (resp) =>
expect(compareSync('abc456', resp.body.password))
expect(compareSync('abc456', resp.body.password)).toBeTruthy()
);
});

Expand Down Expand Up @@ -71,7 +71,9 @@ describe('Password attribute tests', () => {
},
})
.expect(async (resp) =>
expect(compareSync('abc456', resp.body.user.password))
expect(
compareSync('abc456', resp.body.user.password)
).toBeTruthy()
);
});
});