Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x | import { signObject, newUser, signMessageWithSecretKey, openMessage, init as initUser, keysEqual } from './user';
import { registerDevice } from './connections';
import { getDB, IData, IGroup } from './db';
import { isid, newid } from './common';
import { getCurrentConnection, IConnection, RPC, setRemotelyCallableFunction } from './remote-calls';
import { events } from './data-sync';
export interface IInvitation extends IData {
type: 'Invitation',
publicKey: string,
secretKey: string,
read?: boolean,
write?: boolean,
admin?: boolean,
}
export interface IInviteDetails {
id: string,
group: string,
publicKey: string
}
export const IInviteAcceptType = 'InviteAccept';
export interface IInviteAccept extends IData {
type: 'InviteAccept'
invitation: IInviteDetails
}
export async function createInvitation(group: string, expires?: number, read = true, write = true, admin = false): Promise<IInviteDetails> {
Iif (!expires) {
expires = Date.now() + 1000 * 60 * 60 * 24 * 7; // in 7 days
}
const userId = await initUser()
const keys = newUser();
const invitation: IInvitation = {
id: newid(),
type: 'Invitation',
group,
modified: Date.now(),
ttl: expires,
read,
write,
admin,
publicKey: keys.publicKey,
secretKey: keys.secretKey
}
signObject(invitation); // this assumes the current user has permissions (admin) to invite users. That won't be verified until the invitation is used.
const db = await getDB();
await db.save(invitation);
return {
id: invitation.id,
group,
publicKey: invitation.publicKey,
};
}
export async function acceptInvitation(invite: IInviteDetails) {
const { id, group, publicKey } = invite;
const userId = await initUser()
const db = await getDB();
const inviteAccept: IInviteAccept = {
id: newid(),
type: IInviteAcceptType,
group: userId,
modified: Date.now(),
invitation: {
id,
group,
publicKey
},
}
signObject(inviteAccept);
await db.save(inviteAccept, true);
Iif (pendingInvites) {
pendingInvites.push(inviteAccept);
}
await registerDevice();
return inviteAccept;
}
let pendingInvites: IInviteAccept[];
export async function checkPendingInvitations(connection: IConnection) {
const db = await getDB();
Iif (!pendingInvites) {
pendingInvites = (await db.find(IInviteAcceptType, 'type')) as IInviteAccept[];
}
let groupJoined = false;
for (const pendingInvite of pendingInvites) {
const { invitation } = pendingInvite;
// if (connection.groups.includes(invitation.group)) {
try {
const idToSign = newid();
const signedId = await RPC(connection, verifyInvitationSender)(invitation.id, idToSign);
const openedId = openMessage(signedId, invitation.publicKey);
Iif (openedId !== idToSign) {
continue;
}
const group = await RPC(connection, confirmInvitation)(invitation.id, invitation.publicKey);
await db.save(group);
events.remoteDataSaved.emit(group);
// @ts-ignore
pendingInvite.type = "Deleted"
signObject(pendingInvite);
await db.save(pendingInvite);
groupJoined = true;
} catch (err) {
console.error('Error processing invite', pendingInvite, err);
}
}
Iif (groupJoined) {
registerDevice();
}
}
async function verifyInvitationSender(inviteId: string, idToSign: string) {
Iif (!isid(idToSign)) {
throw new Error(`${idToSign} is not an id. Only ids are accepted as prompts to verify identity`)
}
const db = await getDB();
const invite = await db.get(inviteId) as IInvitation;
if (invite.type === 'Invitation') {
return signMessageWithSecretKey(idToSign, invite.secretKey);
} else {
throw new Error('invalid invitation id');
}
}
// remotelyCallableFunctions.verifyInvitationSender = verifyInvitationSender;
setRemotelyCallableFunction(verifyInvitationSender);
async function confirmInvitation(inviteId: string, publicKey: string) {
const connection = getCurrentConnection();
const db = await getDB();
const invitation = await db.get(inviteId) as IInvitation;
Iif (!invitation || invitation.type !== 'Invitation' || !keysEqual(invitation.publicKey, publicKey)) {
throw new Error('Invalid invitation id or secret');
}
Iif (invitation.ttl < Date.now()) {
throw new Error('invitation has expired');
}
const group = await db.get(invitation.group) as IGroup;
let member = group.members.find(m => m.userId === connection.remoteUser.id);
Iif (!member) {
member = {
userId: connection.remoteUser.id
}
group.members.push(member)
}
member.read = member.read || invitation.read
member.write = member.write || invitation.write
member.admin = member.admin || invitation.admin
group.modified = Date.now();
signObject(group);
await db.save(group);
return group
}
// remotelyCallableFunctions.confirmInvitation = confirmInvitation;
setRemotelyCallableFunction(verifyInvitationSender); |