@joystick.js/db-canary 0.0.0-canary.2295 → 0.0.0-canary.2297
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/lib/operations/admin.js +1 -1
- package/dist/server/lib/user_auth_manager.js +1 -0
- package/package.json +2 -2
- package/src/client/index.js +21 -7
- package/src/server/index.js +60 -19
- package/src/server/lib/operations/admin.js +91 -2
- package/src/server/lib/user_auth_manager.js +745 -0
- package/tests/client/index.test.js +468 -69
- package/tests/server/index.test.js +210 -43
- package/tests/server/integration/authentication_integration.test.js +65 -33
- package/tests/server/integration/development_mode_authentication.test.js +21 -7
- package/tests/server/integration/production_safety_integration.test.js +24 -34
- package/tests/server/integration/replication_integration.test.js +17 -25
- package/tests/server/lib/operations/admin.test.js +39 -5
- package/tests/server/lib/user_auth_manager.test.js +525 -0
|
@@ -3,21 +3,17 @@ import net from 'net';
|
|
|
3
3
|
import { create_server } from '../../../src/server/index.js';
|
|
4
4
|
import { encode_message, create_message_parser } from '../../../src/server/lib/tcp_protocol.js';
|
|
5
5
|
import { initialize_database } from '../../../src/server/lib/query_engine.js';
|
|
6
|
-
import {
|
|
6
|
+
import { setup_initial_admin, reset_auth_state } from '../../../src/server/lib/user_auth_manager.js';
|
|
7
7
|
|
|
8
8
|
let server;
|
|
9
9
|
let port;
|
|
10
|
-
let
|
|
10
|
+
let admin_username = 'admin';
|
|
11
|
+
let admin_password = 'admin123';
|
|
11
12
|
|
|
12
13
|
test.before(async () => {
|
|
13
14
|
try {
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
const { unlinkSync } = await import('fs');
|
|
17
|
-
unlinkSync('authentication.json');
|
|
18
|
-
} catch (error) {
|
|
19
|
-
// File doesn't exist, which is fine
|
|
20
|
-
}
|
|
15
|
+
// Reset auth state
|
|
16
|
+
await reset_auth_state();
|
|
21
17
|
|
|
22
18
|
// Initialize database for testing
|
|
23
19
|
initialize_database();
|
|
@@ -38,17 +34,8 @@ test.before(async () => {
|
|
|
38
34
|
});
|
|
39
35
|
});
|
|
40
36
|
|
|
41
|
-
// Set up
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
send({ op: 'setup' });
|
|
45
|
-
const setup_response = await receive();
|
|
46
|
-
if (setup_response.ok === 1 || setup_response.ok === true) {
|
|
47
|
-
shared_password = setup_response.password;
|
|
48
|
-
}
|
|
49
|
-
} finally {
|
|
50
|
-
close();
|
|
51
|
-
}
|
|
37
|
+
// Set up initial admin user for all tests
|
|
38
|
+
await setup_initial_admin(admin_username, admin_password, 'admin@test.com');
|
|
52
39
|
} catch (error) {
|
|
53
40
|
throw error;
|
|
54
41
|
}
|
|
@@ -61,6 +48,9 @@ test.after.always(async () => {
|
|
|
61
48
|
server.close(resolve);
|
|
62
49
|
});
|
|
63
50
|
}
|
|
51
|
+
|
|
52
|
+
// Clean up auth state
|
|
53
|
+
await reset_auth_state();
|
|
64
54
|
});
|
|
65
55
|
|
|
66
56
|
const create_client = () => {
|
|
@@ -122,8 +112,8 @@ test('production safety - admin stats include comprehensive monitoring', async t
|
|
|
122
112
|
const { client, send, receive, close } = await create_client();
|
|
123
113
|
|
|
124
114
|
try {
|
|
125
|
-
// Authenticate using
|
|
126
|
-
send({ op: 'authentication', data: { password:
|
|
115
|
+
// Authenticate using username and password
|
|
116
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
127
117
|
const auth_response = await receive();
|
|
128
118
|
t.is(auth_response.ok, 1);
|
|
129
119
|
|
|
@@ -187,8 +177,8 @@ test('production safety - request timeout handling', async t => {
|
|
|
187
177
|
const { client, send, receive, close } = await create_client();
|
|
188
178
|
|
|
189
179
|
try {
|
|
190
|
-
// Authenticate using
|
|
191
|
-
send({ op: 'authentication', data: { password:
|
|
180
|
+
// Authenticate using username and password
|
|
181
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
192
182
|
const auth_response = await receive();
|
|
193
183
|
t.is(auth_response.ok, 1);
|
|
194
184
|
|
|
@@ -226,8 +216,8 @@ test('production safety - structured logging for operations', async t => {
|
|
|
226
216
|
const { client, send, receive, close } = await create_client();
|
|
227
217
|
|
|
228
218
|
try {
|
|
229
|
-
// Authenticate using
|
|
230
|
-
send({ op: 'authentication', data: { password:
|
|
219
|
+
// Authenticate using username and password
|
|
220
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
231
221
|
const auth_response = await receive();
|
|
232
222
|
t.is(auth_response.ok, 1);
|
|
233
223
|
|
|
@@ -250,8 +240,8 @@ test('production safety - error handling and categorization', async t => {
|
|
|
250
240
|
const { client, send, receive, close } = await create_client();
|
|
251
241
|
|
|
252
242
|
try {
|
|
253
|
-
// Authenticate using
|
|
254
|
-
send({ op: 'authentication', data: { password:
|
|
243
|
+
// Authenticate using username and password
|
|
244
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
255
245
|
const auth_response = await receive();
|
|
256
246
|
t.is(auth_response.ok, 1);
|
|
257
247
|
|
|
@@ -271,8 +261,8 @@ test('production safety - performance metrics tracking', async t => {
|
|
|
271
261
|
const { client, send, receive, close } = await create_client();
|
|
272
262
|
|
|
273
263
|
try {
|
|
274
|
-
// Authenticate using
|
|
275
|
-
send({ op: 'authentication', data: { password:
|
|
264
|
+
// Authenticate using username and password
|
|
265
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
276
266
|
const auth_response = await receive();
|
|
277
267
|
t.is(auth_response.ok, 1);
|
|
278
268
|
|
|
@@ -298,8 +288,8 @@ test('production safety - memory and resource monitoring', async t => {
|
|
|
298
288
|
const { client, send, receive, close } = await create_client();
|
|
299
289
|
|
|
300
290
|
try {
|
|
301
|
-
// Authenticate using
|
|
302
|
-
send({ op: 'authentication', data: { password:
|
|
291
|
+
// Authenticate using username and password
|
|
292
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
303
293
|
const auth_response = await receive();
|
|
304
294
|
t.is(auth_response.ok, 1);
|
|
305
295
|
|
|
@@ -332,8 +322,8 @@ test('production safety - connection management stats', async t => {
|
|
|
332
322
|
const { client, send, receive, close } = await create_client();
|
|
333
323
|
|
|
334
324
|
try {
|
|
335
|
-
// Authenticate using
|
|
336
|
-
send({ op: 'authentication', data: { password:
|
|
325
|
+
// Authenticate using username and password
|
|
326
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
337
327
|
const auth_response = await receive();
|
|
338
328
|
t.is(auth_response.ok, 1);
|
|
339
329
|
|
|
@@ -2,7 +2,7 @@ import test from 'ava';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import net from 'net';
|
|
4
4
|
import { create_server } from '../../../src/server/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { setup_initial_admin, reset_auth_state } from '../../../src/server/lib/user_auth_manager.js';
|
|
6
6
|
import { initialize_database } from '../../../src/server/lib/query_engine.js';
|
|
7
7
|
import { encode_message, create_message_parser } from '../../../src/server/lib/tcp_protocol.js';
|
|
8
8
|
|
|
@@ -11,14 +11,15 @@ const AUTH_FILE_PATH = './settings.db.json';
|
|
|
11
11
|
// Shared server instance
|
|
12
12
|
let shared_server = null;
|
|
13
13
|
let shared_port = null;
|
|
14
|
-
let
|
|
14
|
+
let admin_username = 'admin';
|
|
15
|
+
let admin_password = 'admin123';
|
|
15
16
|
|
|
16
17
|
test.before(async () => {
|
|
17
18
|
// Set test environment
|
|
18
19
|
process.env.NODE_ENV = 'test';
|
|
19
20
|
|
|
20
21
|
// Reset auth state and clean up any existing auth files
|
|
21
|
-
reset_auth_state();
|
|
22
|
+
await reset_auth_state();
|
|
22
23
|
if (fs.existsSync(AUTH_FILE_PATH)) {
|
|
23
24
|
fs.unlinkSync(AUTH_FILE_PATH);
|
|
24
25
|
}
|
|
@@ -45,6 +46,9 @@ test.before(async () => {
|
|
|
45
46
|
}
|
|
46
47
|
});
|
|
47
48
|
});
|
|
49
|
+
|
|
50
|
+
// Set up initial admin user
|
|
51
|
+
await setup_initial_admin(admin_username, admin_password, 'admin@test.com');
|
|
48
52
|
});
|
|
49
53
|
|
|
50
54
|
test.after.always(async () => {
|
|
@@ -83,7 +87,7 @@ test.after.always(async () => {
|
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// Reset auth state and clean up auth files
|
|
86
|
-
reset_auth_state();
|
|
90
|
+
await reset_auth_state();
|
|
87
91
|
if (fs.existsSync(AUTH_FILE_PATH)) {
|
|
88
92
|
try {
|
|
89
93
|
fs.unlinkSync(AUTH_FILE_PATH);
|
|
@@ -98,12 +102,9 @@ test.after.always(async () => {
|
|
|
98
102
|
}
|
|
99
103
|
});
|
|
100
104
|
|
|
101
|
-
test.beforeEach(() => {
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
if (fs.existsSync(AUTH_FILE_PATH)) {
|
|
105
|
-
fs.unlinkSync(AUTH_FILE_PATH);
|
|
106
|
-
}
|
|
105
|
+
test.beforeEach(async () => {
|
|
106
|
+
// Ensure admin user is set up for each test
|
|
107
|
+
await setup_initial_admin(admin_username, admin_password, 'admin@test.com');
|
|
107
108
|
});
|
|
108
109
|
|
|
109
110
|
const create_client = () => {
|
|
@@ -146,14 +147,11 @@ const create_client = () => {
|
|
|
146
147
|
};
|
|
147
148
|
|
|
148
149
|
test('replication admin operations - get status when disabled', async t => {
|
|
149
|
-
// Setup authentication first
|
|
150
|
-
const password = setup_authentication();
|
|
151
|
-
|
|
152
150
|
const { client, send, receive, close } = await create_client();
|
|
153
151
|
|
|
154
152
|
try {
|
|
155
|
-
// First authenticate
|
|
156
|
-
send({ op: 'authentication', data: { password } });
|
|
153
|
+
// First authenticate with username and password
|
|
154
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
157
155
|
const auth_response = await receive();
|
|
158
156
|
t.is(auth_response.ok, 1);
|
|
159
157
|
|
|
@@ -171,14 +169,11 @@ test('replication admin operations - get status when disabled', async t => {
|
|
|
171
169
|
});
|
|
172
170
|
|
|
173
171
|
test('replication integration - basic server functionality works', async t => {
|
|
174
|
-
// Setup authentication first
|
|
175
|
-
const password = setup_authentication();
|
|
176
|
-
|
|
177
172
|
const { client, send, receive, close } = await create_client();
|
|
178
173
|
|
|
179
174
|
try {
|
|
180
|
-
// First authenticate
|
|
181
|
-
send({ op: 'authentication', data: { password } });
|
|
175
|
+
// First authenticate with username and password
|
|
176
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
182
177
|
const auth_response = await receive();
|
|
183
178
|
t.is(auth_response.ok, 1);
|
|
184
179
|
|
|
@@ -200,14 +195,11 @@ test('replication integration - basic server functionality works', async t => {
|
|
|
200
195
|
});
|
|
201
196
|
|
|
202
197
|
test('replication integration - database operations work normally', async t => {
|
|
203
|
-
// Setup authentication first
|
|
204
|
-
const password = setup_authentication();
|
|
205
|
-
|
|
206
198
|
const { client, send, receive, close } = await create_client();
|
|
207
199
|
|
|
208
200
|
try {
|
|
209
|
-
// First authenticate
|
|
210
|
-
send({ op: 'authentication', data: { password } });
|
|
201
|
+
// First authenticate with username and password
|
|
202
|
+
send({ op: 'authentication', data: { username: admin_username, password: admin_password } });
|
|
211
203
|
const auth_response = await receive();
|
|
212
204
|
t.is(auth_response.ok, 1);
|
|
213
205
|
|
|
@@ -4,7 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import { encode as encode_messagepack, decode as decode_messagepack } from 'msgpackr';
|
|
5
5
|
import { create_server } from '../../../../src/server/index.js';
|
|
6
6
|
import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
|
|
7
|
-
import { reset_auth_state } from '../../../../src/server/lib/
|
|
7
|
+
import { reset_auth_state } from '../../../../src/server/lib/user_auth_manager.js';
|
|
8
8
|
|
|
9
9
|
// Dynamic port allocation
|
|
10
10
|
let current_port = 4000;
|
|
@@ -17,7 +17,7 @@ let port;
|
|
|
17
17
|
|
|
18
18
|
test.beforeEach(async () => {
|
|
19
19
|
// Reset auth state
|
|
20
|
-
reset_auth_state();
|
|
20
|
+
await reset_auth_state();
|
|
21
21
|
|
|
22
22
|
// Initialize test database and clear it for clean state between tests
|
|
23
23
|
initialize_database('./test_data');
|
|
@@ -51,7 +51,7 @@ test.afterEach(async () => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Clean up auth state after each test
|
|
54
|
-
reset_auth_state();
|
|
54
|
+
await reset_auth_state();
|
|
55
55
|
|
|
56
56
|
// Clean up database
|
|
57
57
|
await cleanup_database();
|
|
@@ -112,7 +112,41 @@ const send_message = (client, message) => {
|
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
const authenticate_client = async (client) => {
|
|
115
|
-
//
|
|
115
|
+
// Try to setup initial admin user first (since tests clean database)
|
|
116
|
+
try {
|
|
117
|
+
const setup_admin_response = await send_message(client, {
|
|
118
|
+
op: 'admin',
|
|
119
|
+
data: {
|
|
120
|
+
admin_action: 'setup_initial_admin',
|
|
121
|
+
username: 'admin',
|
|
122
|
+
password: 'admin123',
|
|
123
|
+
email: 'admin@test.com'
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// If successful or already exists, proceed with authentication
|
|
128
|
+
if (setup_admin_response.ok || setup_admin_response.error?.includes('already exists')) {
|
|
129
|
+
// Now authenticate with username/password
|
|
130
|
+
const auth_response = await send_message(client, {
|
|
131
|
+
op: 'authentication',
|
|
132
|
+
data: {
|
|
133
|
+
username: 'admin',
|
|
134
|
+
password: 'admin123'
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (!auth_response.ok) {
|
|
139
|
+
throw new Error(`Authentication failed: ${JSON.stringify(auth_response.error || auth_response)}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return 'admin123';
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// If admin setup fails, fall back to old setup method for backward compatibility
|
|
146
|
+
console.log('Admin setup failed, trying legacy setup method:', error.message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Fallback: try legacy setup method
|
|
116
150
|
const setup_response = await send_message(client, {
|
|
117
151
|
op: 'setup',
|
|
118
152
|
data: {}
|
|
@@ -127,7 +161,7 @@ const authenticate_client = async (client) => {
|
|
|
127
161
|
throw new Error(`Setup response did not contain password: ${JSON.stringify(setup_response)}`);
|
|
128
162
|
}
|
|
129
163
|
|
|
130
|
-
//
|
|
164
|
+
// Legacy authentication with just password
|
|
131
165
|
const auth_response = await send_message(client, {
|
|
132
166
|
op: 'authentication',
|
|
133
167
|
data: { password }
|