@peers-app/peers-sdk 0.7.23 → 0.7.24

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.
@@ -15,6 +15,7 @@ export declare class UserContext {
15
15
  readonly groupDataContexts: Map<string, DataContext>;
16
16
  readonly defaultDataContext: Observable<DataContext>;
17
17
  readonly loadingPromise: Promise<UserContext>;
18
+ private personalUserSubscription?;
18
19
  constructor(userId: string, dataSourceFactory: DataSourceFactory, ephemeral?: boolean | undefined);
19
20
  private init;
20
21
  /**
@@ -41,6 +42,7 @@ export declare class UserContext {
41
42
  subscribeToDataChangedAcrossAllGroups<T extends {
42
43
  [key: string]: any;
43
44
  }>(table: string | Table<T>, handler: (evt: ICrossGroupSubscriptionHandlerArgs<T>) => any): import("../events").ISubscriptionResult;
45
+ dispose(): void;
44
46
  }
45
47
  export interface ICrossGroupSubscriptionHandlerArgs<T> {
46
48
  name: string;
@@ -20,6 +20,7 @@ class UserContext {
20
20
  groupDataContexts = new Map();
21
21
  defaultDataContext;
22
22
  loadingPromise;
23
+ personalUserSubscription;
23
24
  constructor(userId, dataSourceFactory, ephemeral) {
24
25
  this.userId = userId;
25
26
  this.dataSourceFactory = dataSourceFactory;
@@ -143,11 +144,10 @@ class UserContext {
143
144
  const userContext = this;
144
145
  me ??= await this.getMe();
145
146
  if (!me) {
146
- // throw new Error('My user object was not passed in and was not found in my personal db')
147
147
  console.warn(`My user object was not in db, creating now`);
148
148
  me = {
149
149
  userId,
150
- name: 'Unnamed_1',
150
+ name: 'Unnamed_' + userId.slice(20),
151
151
  publicKey: '',
152
152
  publicBoxKey: '',
153
153
  };
@@ -160,7 +160,7 @@ class UserContext {
160
160
  (0, keys_1.verifyObjectSignature)(meSigned);
161
161
  await (0, data_1.Users)(userContext.userDataContext).save(meSigned);
162
162
  }
163
- // sync my user to all all my groups
163
+ // sync my user to all my groups
164
164
  for (const [, dataContext] of userContext.groupDataContexts) {
165
165
  let groupMe = await (0, data_1.Users)(dataContext).get(me.userId);
166
166
  if (!(0, lodash_1.isEqual)(groupMe, meSigned)) {
@@ -186,6 +186,23 @@ class UserContext {
186
186
  await (0, data_1.Groups)(this.userDataContext).save(groupObject);
187
187
  }
188
188
  }
189
+ if (!this.personalUserSubscription) {
190
+ this.personalUserSubscription = this.subscribeToDataChangedAcrossAllGroups('Users', async (evt) => {
191
+ const changedUser = evt.data.dataObject;
192
+ if (changedUser.userId !== this.userId) {
193
+ // sync to my personal db if this is a user that I have there
194
+ const personalUser = await (0, data_1.Users)(this.userDataContext).get(changedUser.userId);
195
+ if (personalUser && !(0, lodash_1.isEqual)(personalUser, changedUser)) {
196
+ try {
197
+ await (0, data_1.Users)(this.userDataContext).save(changedUser);
198
+ }
199
+ catch (err) {
200
+ console.error(`Error saving user ${changedUser.userId} to personal db:`, err);
201
+ }
202
+ }
203
+ }
204
+ });
205
+ }
189
206
  }
190
207
  subscribeToDataChangedAcrossAllGroups(table, handler) {
191
208
  const tableName = typeof table === 'string' ? table : table.tableName;
@@ -202,5 +219,8 @@ class UserContext {
202
219
  });
203
220
  return subscription;
204
221
  }
222
+ dispose() {
223
+ this.personalUserSubscription?.unsubscribe();
224
+ }
205
225
  }
206
226
  exports.UserContext = UserContext;
@@ -17,6 +17,8 @@ export declare class Connection {
17
17
  timeoutMs: number;
18
18
  maxChunkSize: number;
19
19
  handshakeTimestampToleranceMs: number;
20
+ private _connectionAddress;
21
+ get connectionAddress(): string | undefined;
20
22
  readonly socket: ISocket;
21
23
  constructor(socket: ISocket, localDevice: Device, localDeviceServerAddresses?: string[] | undefined, getTrustLevel?: GetTrustLevel);
22
24
  get verified(): boolean;
@@ -38,6 +40,7 @@ export declare class Connection {
38
40
  completeHandshake(boxedHandshake: IDataBox): Promise<IDeviceHandshake>;
39
41
  doHandshake(remoteAddress: string): Promise<IDeviceHandshake>;
40
42
  private closeLocal;
43
+ ping(): Promise<number>;
41
44
  private closed;
42
45
  close(): Promise<void>;
43
46
  }
@@ -30,6 +30,10 @@ class Connection {
30
30
  timeoutMs = 60_000; // 60 seconds
31
31
  maxChunkSize = socket_type_1.DEFAULT_MAX_CHUNK_SIZE;
32
32
  handshakeTimestampToleranceMs = HANDSHAKE_TIMESTAMP_TOLERANCE_MS;
33
+ _connectionAddress;
34
+ get connectionAddress() {
35
+ return this._connectionAddress;
36
+ }
33
37
  socket;
34
38
  constructor(socket, localDevice, localDeviceServerAddresses, getTrustLevel = () => Promise.resolve(socket_type_1.TrustLevel.Unknown)) {
35
39
  this.localDevice = localDevice;
@@ -47,6 +51,9 @@ class Connection {
47
51
  this.closeLocal();
48
52
  });
49
53
  });
54
+ this.exposeRPC('ping', () => {
55
+ return Date.now();
56
+ });
50
57
  if (localDeviceServerAddresses) {
51
58
  this.exposeRPC('reset', this.reset.bind(this));
52
59
  this.exposeRPC('getTrustLevel', async (deviceInfo) => {
@@ -171,6 +178,7 @@ class Connection {
171
178
  this.socket.removeAllListeners(eventName);
172
179
  }
173
180
  reset() {
181
+ this._connectionAddress = undefined;
174
182
  this._remoteDeviceInfo = {
175
183
  deviceId: '',
176
184
  userId: '',
@@ -203,6 +211,7 @@ class Connection {
203
211
  'unknown';
204
212
  // only set local secure if the server address matches the client's address and the address is using https
205
213
  this.secureLocal = clientServerAddress === thisServerAddress && thisServerAddress.startsWith('https');
214
+ this._connectionAddress = thisServerAddress;
206
215
  const handshakeResponse = this.localDevice.handshakeResponse(signedHandshake, this.connectionId, thisServerAddress);
207
216
  this._remoteDeviceInfo = {
208
217
  userId: _handshake.userId,
@@ -212,6 +221,8 @@ class Connection {
212
221
  };
213
222
  this.trustLevel = await this.getTrustLevel(signedHandshake.contents, true);
214
223
  if (this.trustLevel < socket_type_1.TrustLevel.Unknown) {
224
+ this.reset();
225
+ this.emit('reset');
215
226
  throw new Error('Untrusted connection');
216
227
  }
217
228
  console.log(`Connection ${this.connectionId} verified on server side with trust level ${this.trustLevel}`);
@@ -242,6 +253,7 @@ class Connection {
242
253
  throw new Error('Inconsistent device info');
243
254
  }
244
255
  this._verified = true;
256
+ this._connectionAddress = remoteAddress;
245
257
  this.secureLocal = handshakeResponse.serverAddress === remoteAddress && remoteAddress.startsWith('https');
246
258
  if (this.secureLocal) {
247
259
  await this.emit('requestSecure');
@@ -260,6 +272,14 @@ class Connection {
260
272
  this.removeAllListeners();
261
273
  this.socket.disconnect(true);
262
274
  }
275
+ async ping() {
276
+ if (!this._verified) {
277
+ throw new Error('Cannot ping unverified connection');
278
+ }
279
+ const startTime = Date.now();
280
+ await this.emit('ping');
281
+ return Date.now() - startTime;
282
+ }
263
283
  closed = false;
264
284
  async close() {
265
285
  if (this.closed)
@@ -267,7 +287,8 @@ class Connection {
267
287
  this.closed = true;
268
288
  await Promise.race([
269
289
  this.emit('close').catch(err => {
270
- console.error('Error sending close event to remote device', err);
290
+ // swallow error
291
+ // console.error('Error sending close event to remote device', err);
271
292
  }),
272
293
  (0, utils_1.sleep)(100),
273
294
  ]);
@@ -0,0 +1,3 @@
1
+ import { IDeviceInfo } from "../data";
2
+ import { TrustLevel } from "./socket.type";
3
+ export declare function getTrustLevelFn(me: Pick<IDeviceInfo, 'userId' | 'publicKey' | 'publicBoxKey'>, serverUrl?: string): (deviceInfo: IDeviceInfo, registerNew?: boolean) => Promise<TrustLevel>;
@@ -8,6 +8,7 @@ function getTrustLevelFn(me, serverUrl) {
8
8
  return async function getTrustLevel(deviceInfo, registerNew) {
9
9
  const userContext = await (0, context_1.getUserContext)();
10
10
  const userDataContext = userContext.userDataContext;
11
+ // if this is my own device it is trusted
11
12
  if (deviceInfo.userId === me.userId && deviceInfo.publicKey === me.publicKey && deviceInfo.publicBoxKey === me.publicBoxKey) {
12
13
  // if (deviceInfo.deviceId === thisDeviceId()) {
13
14
  // return TrustLevel.Untrusted;
@@ -23,53 +24,67 @@ function getTrustLevelFn(me, serverUrl) {
23
24
  device.lastSeen = new Date();
24
25
  device.trustLevel = socket_type_1.TrustLevel.Trusted;
25
26
  console.log(`Updating my own device: ${deviceInfo.deviceId}`);
26
- await (0, data_1.Devices)(userDataContext).save(device);
27
+ await (0, data_1.Devices)(userDataContext).save(device, { restoreIfDeleted: true });
27
28
  return socket_type_1.TrustLevel.Trusted;
28
29
  }
29
- // await Devices().delete(deviceInfo.deviceId);
30
30
  let [user, device, userTrustLevel] = await Promise.all([
31
31
  (0, data_1.Users)(userDataContext).get(deviceInfo.userId),
32
32
  (0, data_1.Devices)(userDataContext).get(deviceInfo.deviceId),
33
33
  (0, data_1.UserTrustLevels)(userDataContext).get(deviceInfo.userId),
34
34
  ]);
35
+ if (device && device.trustLevel < socket_type_1.TrustLevel.Unknown) {
36
+ // If device is untrusted, return immediately
37
+ return device.trustLevel;
38
+ }
39
+ // TODO check user trust level, if they are untrusted, return immediately
35
40
  if (user && device && !(deviceInfo.userId === device.userId && deviceInfo.userId === user.userId && deviceInfo.publicKey === user.publicKey && deviceInfo.publicBoxKey === user.publicBoxKey)) {
36
- console.error(new Date().toISOString(), 'deviceInfo does not align with local info about user and device', { deviceInfo, user, device });
41
+ // deviceInfo does not align with local info about user and device, return Untrusted
37
42
  // TODO check if user has changed their public keys
38
43
  return socket_type_1.TrustLevel.Untrusted;
39
44
  }
40
45
  if (userTrustLevel?.trustLevel && userTrustLevel.trustLevel >= socket_type_1.TrustLevel.Trusted && device?.trustLevel && device.trustLevel >= socket_type_1.TrustLevel.Trusted) {
41
46
  device.lastSeen = new Date();
42
- await (0, data_1.Devices)(userDataContext).update(device);
47
+ await (0, data_1.Devices)(userDataContext).save(device, { restoreIfDeleted: true });
43
48
  return socket_type_1.TrustLevel.Trusted;
44
49
  }
45
- let trustLevel = device?.trustLevel ?? socket_type_1.TrustLevel.Known;
50
+ let trustLevel = device?.trustLevel ?? socket_type_1.TrustLevel.Unknown;
51
+ const lastSeen = new Date();
46
52
  if (!device) {
47
53
  device = {
48
54
  deviceId: deviceInfo.deviceId,
49
55
  userId: deviceInfo.userId,
50
- firstSeen: new Date(),
51
- lastSeen: new Date(),
56
+ firstSeen: lastSeen,
57
+ lastSeen,
52
58
  serverUrl,
53
- trustLevel: socket_type_1.TrustLevel.Unknown,
59
+ trustLevel: socket_type_1.TrustLevel.NewDevice,
54
60
  };
55
- trustLevel = socket_type_1.TrustLevel.NewDevice;
56
61
  }
57
62
  else {
58
- device.lastSeen = new Date();
63
+ device.lastSeen = lastSeen;
64
+ if (device.trustLevel < socket_type_1.TrustLevel.NewDevice) {
65
+ device.trustLevel = socket_type_1.TrustLevel.NewDevice;
66
+ }
67
+ const knownThreshold = 1000 * 60 * 60 * 24 * 7; // 7 days
68
+ const passedKnownThreshold = Date.now() - device.firstSeen.getTime() > knownThreshold;
69
+ // const passedKnownThreshold = true; // TODO - for testing, remove
70
+ if (passedKnownThreshold && device.trustLevel < socket_type_1.TrustLevel.Known) {
71
+ device.trustLevel = socket_type_1.TrustLevel.Known;
72
+ }
59
73
  }
74
+ trustLevel = device.trustLevel;
60
75
  let newUser = false;
61
76
  if (!user) {
62
77
  user = {
63
78
  userId: deviceInfo.userId,
64
79
  publicKey: deviceInfo.publicKey,
65
80
  publicBoxKey: deviceInfo.publicBoxKey,
66
- name: serverUrl || deviceInfo.userId,
81
+ name: serverUrl || `Unnamed_${deviceInfo.userId.substring(20)}`,
67
82
  };
68
83
  trustLevel = socket_type_1.TrustLevel.NewUser;
69
84
  newUser = true;
70
85
  }
71
- let remoteTrustLevel = socket_type_1.TrustLevel.Unknown;
72
86
  // TODO - reimplement this, checks with peers to see if this is an untrusted device
87
+ // let remoteTrustLevel = TrustLevel.Unknown;
73
88
  // for (const [serverUrl, conn] of Object.entries(getActiveConnections())) {
74
89
  // if (conn.trustLevel >= TrustLevel.Trusted) {
75
90
  // const _remoteTrustLevel: TrustLevel = await conn.emit('getTrustLevel', deviceInfo).catch(err => {
@@ -89,18 +104,19 @@ function getTrustLevelFn(me, serverUrl) {
89
104
  // }
90
105
  // }
91
106
  // }
92
- if (remoteTrustLevel > socket_type_1.TrustLevel.Unknown && !userTrustLevel) {
93
- await (0, data_1.UserTrustLevels)(userDataContext).save({
94
- userId: deviceInfo.userId,
95
- trustLevel: socket_type_1.TrustLevel.Known,
96
- assignedAt: new Date(),
97
- });
98
- }
99
- else if (newUser) {
100
- await (0, data_1.Users)(userDataContext).save(user);
107
+ // if (remoteTrustLevel && !userTrustLevel) {
108
+ // await UserTrustLevels(userDataContext).save({
109
+ // userId: deviceInfo.userId,
110
+ // trustLevel: remoteTrustLevel,
111
+ // assignedAt: new Date(),
112
+ // }, { restoreIfDeleted: true });
113
+ // }
114
+ if (newUser) {
115
+ // TODO: I'm not sure we immediately want to save this user to the my personal db...
116
+ await (0, data_1.Users)(userDataContext).save(user, { restoreIfDeleted: true });
101
117
  }
102
- device.trustLevel = remoteTrustLevel || trustLevel;
103
- await (0, data_1.Devices)(userDataContext).save(device);
118
+ // device.trustLevel = remoteTrustLevel || trustLevel;
119
+ await (0, data_1.Devices)(userDataContext).save(device, { restoreIfDeleted: true });
104
120
  return device.trustLevel;
105
121
  };
106
122
  }
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export * from "./types/zod-types";
15
15
  export * from "./device/connection";
16
16
  export * from "./device/device";
17
17
  export * from "./device/device-election";
18
- export * from "./device/get-trust-level";
18
+ export * from "./device/get-trust-level-fn";
19
19
  export * from "./device/socket.type";
20
20
  export * from "./device/tx-encoding";
21
21
  export * from "./package-loader";
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ __exportStar(require("./types/zod-types"), exports);
32
32
  __exportStar(require("./device/connection"), exports);
33
33
  __exportStar(require("./device/device"), exports);
34
34
  __exportStar(require("./device/device-election"), exports);
35
- __exportStar(require("./device/get-trust-level"), exports);
35
+ __exportStar(require("./device/get-trust-level-fn"), exports);
36
36
  __exportStar(require("./device/socket.type"), exports);
37
37
  __exportStar(require("./device/tx-encoding"), exports);
38
38
  __exportStar(require("./package-loader"), exports);
@@ -80,16 +80,26 @@ async function usersCursor(filter, opts = {}) {
80
80
  .map(async (user) => ({
81
81
  ...user,
82
82
  source: 'userDataContext',
83
- trustLevel: includeTrustLevel ? await (0, index_1.getUserTrustLevel)(userContext.userDataContext, user.userId) : undefined
83
+ trustLevel: index_1.TrustLevel.Unknown,
84
84
  }));
85
85
  }
86
86
  // Deduplication by userId
87
87
  // TODO: Push deduplication logic down into the queries for better performance
88
88
  const userIdsSeen = new Set();
89
- return finalCursor.filter(user => {
89
+ return finalCursor.filter(async (user) => {
90
90
  if (userIdsSeen.has(user.userId)) {
91
91
  return false;
92
92
  }
93
+ if (includeTrustLevel) {
94
+ // use personal trust level if I have one
95
+ // TODO: Review this, I think this makes sense but it could create scenarios where I am unintentionally
96
+ // trusting another user too much just because a group I'm in has given them a high trust rating
97
+ // IDEA: Assign a trust level to _groups_ and then user trust levels from that group are capped at that level
98
+ const personalTrustLevel = await (0, index_1.getUserTrustLevel)(userContext.userDataContext, user.userId);
99
+ if (personalTrustLevel) {
100
+ user.trustLevel = personalTrustLevel;
101
+ }
102
+ }
93
103
  userIdsSeen.add(user.userId);
94
104
  return true;
95
105
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.7.23",
3
+ "version": "0.7.24",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"
@@ -1,3 +0,0 @@
1
- import { IDeviceInfo, IUser } from "../data";
2
- import { TrustLevel } from "./socket.type";
3
- export declare function getTrustLevelFn(me: IUser, serverUrl?: string): (deviceInfo: IDeviceInfo, registerNew?: boolean) => Promise<TrustLevel>;