Skip to content
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
38 changes: 35 additions & 3 deletions foreign/node/src/e2e/tcp.user.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,38 @@ describe('e2e -> user', async () => {
PollMessages: true,
SendMessages: true
},
streams: []
streams: [
{
streamId: 1,
permissions: {
manageStream: true,
readStream: true,
manageTopics: true,
readTopics: true,
pollMessages: true,
sendMessages: true
},
topics: [
{
topicId: 1,
permissions: {
manage: true,
read: true,
pollMessages: true,
sendMessages: true
}
}
]
}
]
};

const cUser = { userId, username, password, status, permissions };

it('e2e -> user::create', async () => {
const user = await c.user.create(cUser);
assert.ok(user);
assert.deepEqual(user.permissions, permissions);
});

it('e2e -> user::list', async () => {
Expand Down Expand Up @@ -89,12 +113,20 @@ describe('e2e -> user', async () => {
it('e2e -> user::updatePermissions', async () => {
const user = await c.user.get({ userId: username });
assert.ok(user);
const perms2 = { ...permissions };
perms2.global.ReadServers = true;
const perms2 = {
...permissions,
global: {
...permissions.global,
ReadServers: true
}
};
const u2 = await c.user.updatePermissions({
userId: user.id, permissions: perms2
});
assert.ok(u2);

const updatedUser = await c.user.get({ userId: user.id });
assert.deepEqual(updatedUser?.permissions, perms2);
});

it('e2e -> user::delete', async () => {
Expand Down
40 changes: 32 additions & 8 deletions foreign/node/src/wire/user/create-user.command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,49 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { CREATE_USER } from './create-user.command.js';
import type { UserPermissions } from './permissions.utils.js';

describe('CreateUser', () => {

describe('serialize', () => {

const u1 = {
id: 1,
username: 'test-user',
password: 'test-pwd',
status: 1, // Active,
// perms: undefined // @TODO
};

it('serialize username, password, status, permissions into buffer', () => {
assert.deepEqual(
CREATE_USER.serialize(u1).length,
1 + u1.username.length + 1 + u1.password.length + 1 + 1 + 4 + 1
);
const baseLength = 1 + Buffer.byteLength(u1.username)
+ 1 + Buffer.byteLength(u1.password) + 1;

it('serialize without permissions', () => {
const serialized = CREATE_USER.serialize(u1);
assert.equal(serialized.length, baseLength + 1);
assert.equal(serialized.readUInt8(baseLength), 0);
});

it('serialize with permissions', () => {
const permissions: UserPermissions = {
global: {
ManageServers: false,
ReadServers: false,
ManageUsers: false,
ReadUsers: false,
ManageStreams: false,
ReadStreams: false,
ManageTopics: false,
ReadTopics: false,
PollMessages: false,
SendMessages: false
},
streams: []
};
const serialized = CREATE_USER.serialize({ ...u1, permissions });

assert.equal(serialized.length, baseLength + 1 + 4 + 11);
assert.equal(serialized.readUInt8(baseLength), 1);
assert.equal(serialized.readUInt32LE(baseLength + 1), 11);
assert.deepEqual(serialized.subarray(baseLength + 5), Buffer.alloc(11));
});

it('throw on username < 1', () => {
Expand Down Expand Up @@ -83,4 +108,3 @@ describe('CreateUser', () => {

});
});

14 changes: 10 additions & 4 deletions foreign/node/src/wire/user/create-user.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,21 @@ export const CREATE_USER = {
if (bPassword.length < 1 || bPassword.length > 255)
throw new Error('User password should be between 1 and 255 bytes');

const bPermissions = serializePermissions(permissions);

return Buffer.concat([
const bUser = Buffer.concat([
uint8ToBuf(bUsername.length),
bUsername,
uint8ToBuf(bPassword.length),
bPassword,
uint8ToBuf(status),
boolToBuf(!!permissions),
]);

if (!permissions)
return Buffer.concat([bUser, boolToBuf(false)]);

const bPermissions = serializePermissions(permissions);
return Buffer.concat([
bUser,
boolToBuf(true),
uint32ToBuf(bPermissions.length),
bPermissions
]);
Expand Down
118 changes: 99 additions & 19 deletions foreign/node/src/wire/user/permissions.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,110 @@

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { serializePermissions, deserializePermissions } from './permissions.utils.js';
import {
deserializePermissions,
serializePermissions,
type UserPermissions
} from './permissions.utils.js';

describe('Permissions', () => {

const permissions = {
global: {
ManageServers: true,
ReadServers: true,
ManageUsers: true,
ReadUsers: true,
ManageStreams: true,
ReadStreams: true,
ManageTopics: true,
ReadTopics: true,
PollMessages: true,
SendMessages: true
},
streams: []
const globalPermissions = {
ManageServers: true,
ReadServers: true,
ManageUsers: true,
ReadUsers: true,
ManageStreams: true,
ReadStreams: true,
ManageTopics: true,
ReadTopics: true,
PollMessages: true,
SendMessages: true
};

it('serialize/deserialize', () => {
const s = serializePermissions(permissions);
const d = deserializePermissions(s);
assert.deepEqual(permissions, d);
const globalOnlyPermissions = {
global: globalPermissions,
streams: []
} satisfies UserPermissions;

const scopedPermissions = {
global: globalPermissions,
streams: [
{
streamId: 1,
permissions: {
manageStream: true,
readStream: false,
manageTopics: true,
readTopics: false,
pollMessages: true,
sendMessages: false
},
topics: [
{
topicId: 10,
permissions: {
manage: true,
read: false,
pollMessages: true,
sendMessages: false
}
},
{
topicId: 20,
permissions: {
manage: false,
read: true,
pollMessages: false,
sendMessages: true
}
}
]
},
{
streamId: 2,
permissions: {
manageStream: false,
readStream: true,
manageTopics: false,
readTopics: true,
pollMessages: false,
sendMessages: true
},
topics: []
}
]
} satisfies UserPermissions;

const serializedScopedPermissions = Buffer.from([
// Global permissions and has_streams.
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// Stream 1, its permissions, and has_topics.
1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
// Topic 10 and has_next_topic.
10, 0, 0, 0, 1, 0, 1, 0, 1,
// Topic 20 and has_next_topic.
20, 0, 0, 0, 0, 1, 0, 1, 0,
// has_next_stream.
1,
// Stream 2, its permissions, has_topics, and has_next_stream.
2, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0
]);

it('round-trips global permissions', () => {
const serialized = serializePermissions(globalOnlyPermissions);
const deserialized = deserializePermissions(serialized);
assert.deepEqual(deserialized, globalOnlyPermissions);
});

it('serializes stream and topic continuation markers', () => {
const serialized = serializePermissions(scopedPermissions);
assert.deepEqual(serialized, serializedScopedPermissions);
});

it('round-trips multiple streams and topics', () => {
const deserialized = deserializePermissions(serializedScopedPermissions);
assert.deepEqual(deserialized, scopedPermissions);
});

});
Loading
Loading