Files: 8299fab7cda81af8f07aacbeb7374249c6e8d848 / index.js
4439 bytesRaw
1 | var I = require('./valid') |
2 | |
3 | /* |
4 | uxer (someone who observes an invite, but not directly involved): |
5 | |
6 | if we see |
7 | someone post an invite I |
8 | someone else post a confirmation C that I has been accepted A |
9 | // OOO that A |
10 | emded that A inside a C(A) |
11 | match valid I->A's and interpret them like follows. |
12 | |
13 | we only care about confirmations if it's of an invite we follow, |
14 | and it hasn't been confirmed already. |
15 | |
16 | { |
17 | invited: { |
18 | <alice>: { <bob>: true, ...} |
19 | } |
20 | |
21 | invites: { <invites>, ...} |
22 | accepts: { <accepts>, ...} |
23 | |
24 | } |
25 | |
26 | --- |
27 | |
28 | pub |
29 | |
30 | someone connects, using key from an open invite I |
31 | they request that invite I (by it's id) |
32 | they send a message accepting A the invite. |
33 | the pub then posts confirmation (I,A) |
34 | |
35 | */ |
36 | |
37 | exports.name = 'invites' |
38 | |
39 | exports.version = '1.0.0' |
40 | exports.manifest = { |
41 | |
42 | } |
43 | |
44 | // KNOWN BUG: it's possible to accept an invite more than once, |
45 | // but peers will ignore subsequent acceptances. it's possible |
46 | // that this could create confusion in certain situations. |
47 | // (but you'd get a feed that some peers thought was invited by Alice |
48 | // other peers would think a different feed accepted that invite) |
49 | // I guess the answer is to let alice reject the second invite?) |
50 | // that would be easier to do if this was a levelreduce? (keys: reduce, instead of a single reduce?) |
51 | exports.init = function (sbot, config) { |
52 | |
53 | var index = sbot._flumeUse('invites', Reduce(1, function (acc, data) { |
54 | if(!acc) acc = {invited: {}, invites:{}, accepts: {}} |
55 | |
56 | var msg = data.value |
57 | var invite, accept |
58 | if(msg.content.type === 'invite') { |
59 | invite = msg |
60 | accept = acc.accepts[data.key] |
61 | } |
62 | else if(msg.content.type === 'invite/accept') { |
63 | accept = msg |
64 | invite = acc.invites[accept.content.receipt] |
65 | } |
66 | else if(msg.content.type === 'invite/confirm') { |
67 | accept = msg.content.embed |
68 | invite = acc.invites[accept.content.receipt] |
69 | } |
70 | if(invite && accept) { |
71 | if(invite === true) |
72 | return acc |
73 | try { |
74 | I.validateAccept(accept, invite) |
75 | //delete matched invites, but _only_ if they are valid. |
76 | delete acc.accepts[accept.receipt] |
77 | //but remember that this invite has been processed. |
78 | acc.invites[accept.receipt] = true |
79 | } catch (err) { |
80 | return acc //? or store something? |
81 | } |
82 | } |
83 | else if(invite) |
84 | acc.invites[data.key] = invite |
85 | else if(accept) |
86 | acc.accepts[accept.receipt] = accept |
87 | |
88 | return acc |
89 | |
90 | })) |
91 | |
92 | sbot.auth.hook(function (fn, args) { |
93 | var id = args[0], cb = args[1] |
94 | index.get(function (err, v) { |
95 | if(err) return cb(err) |
96 | for(var k in v.invites) |
97 | if(v.invites[k].invite === id) |
98 | return cb(null, { |
99 | allow: ['invite.getInvite', 'invites.accept'], |
100 | deny: null |
101 | }) |
102 | }) |
103 | }) |
104 | |
105 | invites.getInvite = function (invite_id, cb) { |
106 | var self = this |
107 | invites.get(function (err, v) { |
108 | var invite = v.invites[invite_id] |
109 | if(err) return cb(err) |
110 | if(!invite) |
111 | cb(code( |
112 | new Error('unknown invite:'+invite_id), |
113 | 'unknown-invite' |
114 | )) |
115 | else if(invite === true) |
116 | //TODO just retrive all confirmations we know about |
117 | //via links. |
118 | cb(code( |
119 | new Error('invite already used:'+invite_id), |
120 | 'invite-already-used' |
121 | )) |
122 | //only allow the guest to request their own invite. |
123 | else if(self.id !== invite.content.invite) |
124 | cb(code( |
125 | new Error('invite did not match client id'), |
126 | 'invite-mismatch' |
127 | )) |
128 | else |
129 | cb(null, v.invites[invite_id]) |
130 | }) |
131 | } |
132 | |
133 | var accepted = {} |
134 | |
135 | invites.accept = function (accept, cb) { |
136 | //check if the invite in question hasn't already been accepted. |
137 | invites.get(function (err, v) { |
138 | var invite_id = accept.content.receipt |
139 | var invite = v.invites[invite_id] |
140 | if(invite === true || accepted[invite_id]) |
141 | //TODO: this should return the confirmation, not an error. |
142 | return cb(code( |
143 | new Error('invite already used:'+invite_id), |
144 | 'invite-already-used' |
145 | )) |
146 | |
147 | try { |
148 | I.validateAccept(accept, invite) |
149 | } catch (err) { |
150 | return cb(err) |
151 | } |
152 | //there is a little race condition here |
153 | accepted[invite_id] = true |
154 | sbot.publish({type: 'invite/confirm', embed: accept}, function (err, msg) { |
155 | delete accepted[invite_id] |
156 | cb(err, msg) |
157 | }) |
158 | }) |
159 | } |
160 | |
161 | return invites |
162 | |
163 | } |
164 | |
165 | |
166 |
Built with git-ssb-web