holosphere 1.1.1 → 1.1.3
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/holosphere.js +1298 -390
- package/package.json +2 -2
- package/test/federation.test.js +418 -0
- package/test/holosphere.test.js +850 -109
- package/test/spacesauth.test.js +337 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import HoloSphere from '../holosphere.js';
|
|
2
|
+
import * as h3 from 'h3-js';
|
|
3
|
+
import Gun from 'gun';
|
|
4
|
+
import 'gun/sea';
|
|
5
|
+
|
|
6
|
+
describe('Space Authentication and Authorization', () => {
|
|
7
|
+
// Global HoloSphere instances
|
|
8
|
+
let holoSphere;
|
|
9
|
+
let strictHoloSphere;
|
|
10
|
+
const testAppName = 'test-auth-app';
|
|
11
|
+
const testCredentials = {
|
|
12
|
+
spacename: 'testspace@example.com',
|
|
13
|
+
password: 'TestPassword123!'
|
|
14
|
+
};
|
|
15
|
+
const testHolon = h3.latLngToCell(40.7128, -74.0060, 7);
|
|
16
|
+
const testLens = 'authTestLens';
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
// Create global instances
|
|
20
|
+
holoSphere = new HoloSphere(testAppName, false);
|
|
21
|
+
strictHoloSphere = new HoloSphere(testAppName, true);
|
|
22
|
+
|
|
23
|
+
// Clean up any existing test spaces
|
|
24
|
+
try {
|
|
25
|
+
await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
|
|
26
|
+
await holoSphere.deleteGlobal('spaces', 'newspace@example.com');
|
|
27
|
+
await holoSphere.deleteGlobal('spaces', 'sharedspace@example.com');
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.log('Cleanup error (can be ignored):', error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create a test space
|
|
33
|
+
await holoSphere.createSpace(testCredentials.spacename, testCredentials.password);
|
|
34
|
+
}, 10000); // Increase timeout for SEA operations
|
|
35
|
+
|
|
36
|
+
describe('Space Management with SEA', () => {
|
|
37
|
+
test('should create a new space with SEA authentication', async () => {
|
|
38
|
+
const newSpace = {
|
|
39
|
+
spacename: 'newspace@example.com',
|
|
40
|
+
password: 'NewPassword123!'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const result = await holoSphere.createSpace(newSpace.spacename, newSpace.password);
|
|
44
|
+
expect(result).toBeTruthy();
|
|
45
|
+
|
|
46
|
+
// Verify SEA data structure
|
|
47
|
+
const spaceData = await holoSphere.getGlobal('spaces', newSpace.spacename);
|
|
48
|
+
expect(spaceData).toBeDefined();
|
|
49
|
+
expect(spaceData.auth).toBeDefined();
|
|
50
|
+
expect(spaceData.pub).toBeDefined();
|
|
51
|
+
expect(spaceData.epub).toBeDefined();
|
|
52
|
+
|
|
53
|
+
// Try creating the same space again should fail
|
|
54
|
+
await expect(holoSphere.createSpace(newSpace.spacename, newSpace.password))
|
|
55
|
+
.rejects.toThrow('Space already exists');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should login space with SEA authentication', async () => {
|
|
59
|
+
const result = await holoSphere.login(testCredentials.spacename, testCredentials.password);
|
|
60
|
+
expect(result).toBeTruthy();
|
|
61
|
+
expect(holoSphere.currentSpace).toBeDefined();
|
|
62
|
+
expect(holoSphere.currentSpace.alias).toBe(testCredentials.spacename);
|
|
63
|
+
expect(holoSphere.currentSpace.auth).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should reject login with incorrect credentials using SEA', async () => {
|
|
67
|
+
await expect(holoSphere.login(testCredentials.spacename, 'wrongpassword'))
|
|
68
|
+
.rejects.toThrow('Authentication failed');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should have valid SEA pair after login', async () => {
|
|
72
|
+
await holoSphere.login(testCredentials.spacename, testCredentials.password);
|
|
73
|
+
expect(holoSphere.currentSpace).toBeDefined();
|
|
74
|
+
expect(holoSphere.currentSpace.pub).toBeDefined();
|
|
75
|
+
expect(holoSphere.currentSpace.epub).toBeDefined();
|
|
76
|
+
expect(holoSphere.currentSpace.auth).toBeDefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should logout space', async () => {
|
|
80
|
+
await holoSphere.login(testCredentials.spacename, testCredentials.password);
|
|
81
|
+
expect(holoSphere.currentSpace).toBeDefined();
|
|
82
|
+
|
|
83
|
+
await holoSphere.logout();
|
|
84
|
+
expect(holoSphere.currentSpace).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('Authenticated Data Operations', () => {
|
|
89
|
+
beforeEach(async () => {
|
|
90
|
+
// Ensure both instances are logged out
|
|
91
|
+
if (holoSphere.currentSpace) {
|
|
92
|
+
await holoSphere.logout();
|
|
93
|
+
}
|
|
94
|
+
if (strictHoloSphere.currentSpace) {
|
|
95
|
+
await strictHoloSphere.logout();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Login with test space to holoSphere
|
|
99
|
+
await holoSphere.login(testCredentials.spacename, testCredentials.password);
|
|
100
|
+
|
|
101
|
+
// Set up schema
|
|
102
|
+
const schema = {
|
|
103
|
+
type: 'object',
|
|
104
|
+
properties: {
|
|
105
|
+
id: { type: 'string' },
|
|
106
|
+
data: { type: 'string' },
|
|
107
|
+
owner: { type: 'string' }
|
|
108
|
+
},
|
|
109
|
+
required: ['id', 'data']
|
|
110
|
+
};
|
|
111
|
+
await holoSphere.setSchema(testLens, schema);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should store data with owner information', async () => {
|
|
115
|
+
const testData = {
|
|
116
|
+
id: 'secured-data-1',
|
|
117
|
+
data: 'This is secured content'
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Store data as authenticated space
|
|
121
|
+
await holoSphere.put(testHolon, testLens, testData);
|
|
122
|
+
|
|
123
|
+
// Retrieve data as authenticated space
|
|
124
|
+
const result = await holoSphere.get(testHolon, testLens, testData.id);
|
|
125
|
+
expect(result).toBeDefined();
|
|
126
|
+
expect(result.data).toBe(testData.data);
|
|
127
|
+
expect(result.owner).toBe(testCredentials.spacename);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('should prevent unauthorized access to data', async () => {
|
|
131
|
+
const testData = {
|
|
132
|
+
id: 'secured-data-2',
|
|
133
|
+
data: 'This is private content'
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Store data as authenticated space
|
|
137
|
+
await holoSphere.put(testHolon, testLens, testData);
|
|
138
|
+
|
|
139
|
+
// Try to retrieve data as unauthenticated space
|
|
140
|
+
await strictHoloSphere.logout();
|
|
141
|
+
const result = await strictHoloSphere.get(testHolon, testLens, testData.id);
|
|
142
|
+
expect(result.owner).toBe(testCredentials.spacename);
|
|
143
|
+
expect(result.data).toBe(testData.data);
|
|
144
|
+
expect(result.federation).toBeDefined();
|
|
145
|
+
expect(result.federation.origin).toBe(testCredentials.spacename);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should prevent unauthorized modification of data', async () => {
|
|
149
|
+
const testData = {
|
|
150
|
+
id: 'secured-data-3',
|
|
151
|
+
data: 'Original content'
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Store data as authenticated space
|
|
155
|
+
await holoSphere.put(testHolon, testLens, testData);
|
|
156
|
+
|
|
157
|
+
// Try to modify data as unauthenticated space
|
|
158
|
+
await strictHoloSphere.logout();
|
|
159
|
+
const modifiedData = {
|
|
160
|
+
id: 'secured-data-3',
|
|
161
|
+
data: 'Modified content'
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
await expect(strictHoloSphere.put(testHolon, testLens, modifiedData))
|
|
165
|
+
.rejects.toThrow('Unauthorized to modify this data');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('should prevent unauthorized deletion of data', async () => {
|
|
169
|
+
const testData = {
|
|
170
|
+
id: 'secured-data-4',
|
|
171
|
+
data: 'Content to protect'
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Store data as authenticated space
|
|
175
|
+
await holoSphere.put(testHolon, testLens, testData);
|
|
176
|
+
|
|
177
|
+
// Try to delete data as unauthenticated space
|
|
178
|
+
await strictHoloSphere.logout();
|
|
179
|
+
await expect(strictHoloSphere.delete(testHolon, testLens, testData.id))
|
|
180
|
+
.rejects.toThrow('Unauthorized to delete this data');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('should allow data sharing between spaces', async () => {
|
|
184
|
+
// Create another space
|
|
185
|
+
const otherSpace = {
|
|
186
|
+
spacename: 'sharedspace@example.com',
|
|
187
|
+
password: 'SharedPass123!'
|
|
188
|
+
};
|
|
189
|
+
await holoSphere.createSpace(otherSpace.spacename, otherSpace.password);
|
|
190
|
+
|
|
191
|
+
// Login to the shared space
|
|
192
|
+
await strictHoloSphere.login(otherSpace.spacename, otherSpace.password);
|
|
193
|
+
|
|
194
|
+
const testData = {
|
|
195
|
+
id: 'shared-data-1',
|
|
196
|
+
data: 'This is shared content',
|
|
197
|
+
shared: [otherSpace.spacename]
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Store data as authenticated space with sharing
|
|
201
|
+
await holoSphere.put(testHolon, testLens, testData);
|
|
202
|
+
|
|
203
|
+
// Retrieve data as shared space
|
|
204
|
+
const result = await strictHoloSphere.get(testHolon, testLens, testData.id);
|
|
205
|
+
expect(result).toBeDefined();
|
|
206
|
+
expect(result.data).toBe(testData.data);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
afterEach(async () => {
|
|
210
|
+
// Clean up test data
|
|
211
|
+
await holoSphere.deleteAll(testHolon, testLens);
|
|
212
|
+
|
|
213
|
+
// Logout from both instances
|
|
214
|
+
if (holoSphere.currentSpace) {
|
|
215
|
+
await holoSphere.logout();
|
|
216
|
+
}
|
|
217
|
+
if (strictHoloSphere.currentSpace) {
|
|
218
|
+
await strictHoloSphere.logout();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('Authentication Edge Cases', () => {
|
|
224
|
+
test('should handle expired sessions', async () => {
|
|
225
|
+
await holoSphere.login(testCredentials.spacename, testCredentials.password);
|
|
226
|
+
|
|
227
|
+
// Simulate session expiration
|
|
228
|
+
holoSphere.currentSpace.exp = Date.now() - 1000;
|
|
229
|
+
|
|
230
|
+
// Attempt operation with expired session
|
|
231
|
+
const testData = { id: 'test-expired', data: 'test' };
|
|
232
|
+
await expect(holoSphere.put(testHolon, testLens, testData))
|
|
233
|
+
.rejects.toThrow('Session expired');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('should handle concurrent authentication requests', async () => {
|
|
237
|
+
const promises = Array(5).fill().map(() =>
|
|
238
|
+
holoSphere.login(testCredentials.spacename, testCredentials.password)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const results = await Promise.all(promises);
|
|
242
|
+
expect(results.every(result => result === true)).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('should handle malformed credentials', async () => {
|
|
246
|
+
await expect(holoSphere.login(null, null))
|
|
247
|
+
.rejects.toThrow('Invalid credentials format');
|
|
248
|
+
|
|
249
|
+
await expect(holoSphere.login('', ''))
|
|
250
|
+
.rejects.toThrow('Invalid credentials format');
|
|
251
|
+
|
|
252
|
+
await expect(holoSphere.login(123, {}))
|
|
253
|
+
.rejects.toThrow('Invalid credentials format');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('SEA Encryption Edge Cases', () => {
|
|
258
|
+
test('should handle malformed SEA data', async () => {
|
|
259
|
+
const malformedSpace = {
|
|
260
|
+
spacename: 'malformed@example.com',
|
|
261
|
+
password: 'MalformedPass123!'
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Clean up any existing malformed space first
|
|
265
|
+
await holoSphere.deleteGlobal('spaces', malformedSpace.spacename);
|
|
266
|
+
|
|
267
|
+
// Wait for cleanup to complete
|
|
268
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
269
|
+
|
|
270
|
+
// Create space but manipulate the auth data
|
|
271
|
+
await holoSphere.createSpace(malformedSpace.spacename, malformedSpace.password);
|
|
272
|
+
|
|
273
|
+
// Wait for space creation
|
|
274
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
275
|
+
|
|
276
|
+
// Get and modify the space data
|
|
277
|
+
const spaceData = await holoSphere.getGlobal('spaces', malformedSpace.spacename);
|
|
278
|
+
expect(spaceData).toBeDefined();
|
|
279
|
+
|
|
280
|
+
// Corrupt the auth data
|
|
281
|
+
const corruptedData = {
|
|
282
|
+
...spaceData,
|
|
283
|
+
auth: { corrupted: 'data' }
|
|
284
|
+
};
|
|
285
|
+
await holoSphere.putGlobal('spaces', corruptedData);
|
|
286
|
+
|
|
287
|
+
// Wait for corruption to be saved
|
|
288
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
289
|
+
|
|
290
|
+
// Attempt to login should fail gracefully
|
|
291
|
+
await expect(holoSphere.login(malformedSpace.spacename, malformedSpace.password))
|
|
292
|
+
.rejects.toThrow('Authentication failed');
|
|
293
|
+
|
|
294
|
+
// Clean up after test
|
|
295
|
+
await holoSphere.deleteGlobal('spaces', malformedSpace.spacename);
|
|
296
|
+
}, 15000); // Increase timeout for this specific test
|
|
297
|
+
|
|
298
|
+
test('should handle concurrent SEA operations', async () => {
|
|
299
|
+
const promises = Array(3).fill().map(() =>
|
|
300
|
+
holoSphere.login(testCredentials.spacename, testCredentials.password)
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const results = await Promise.all(promises);
|
|
304
|
+
expect(results.every(result => result === true)).toBeTruthy();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
afterAll(async () => {
|
|
309
|
+
// Final cleanup
|
|
310
|
+
if (holoSphere.currentSpace) {
|
|
311
|
+
await holoSphere.logout();
|
|
312
|
+
}
|
|
313
|
+
if (strictHoloSphere.currentSpace) {
|
|
314
|
+
await strictHoloSphere.logout();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Clear all test data
|
|
318
|
+
await holoSphere.deleteAll(testHolon, testLens);
|
|
319
|
+
|
|
320
|
+
// Clean up test spaces
|
|
321
|
+
try {
|
|
322
|
+
await holoSphere.deleteGlobal('spaces', testCredentials.spacename);
|
|
323
|
+
await holoSphere.deleteGlobal('spaces', 'newspace@example.com');
|
|
324
|
+
await holoSphere.deleteGlobal('spaces', 'sharedspace@example.com');
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.log('Cleanup error (can be ignored):', error);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Clean up Gun instances
|
|
330
|
+
if (holoSphere.gun) {
|
|
331
|
+
holoSphere.gun.off();
|
|
332
|
+
}
|
|
333
|
+
if (strictHoloSphere.gun) {
|
|
334
|
+
strictHoloSphere.gun.off();
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
});
|