UNPKG

8.6 kBJavaScriptView Raw
1'use strict'
2
3const ChatServiceError = require('./ChatServiceError')
4const Promise = require('bluebird')
5const _ = require('lodash')
6const { mixin } = require('es6-mixin')
7const { asyncLimit, run } = require('./utils')
8
9class RoomPermissions {
10
11 constructor (roomName, roomState, emitFailure) {
12 this.roomName = roomName
13 this.roomState = roomState
14 this.emitFailure = emitFailure
15 }
16
17 consistencyFailure (error, operationInfo) {
18 operationInfo.roomName = this.roomName
19 operationInfo.opType = 'roomUserlist'
20 this.emitFailure('storeConsistencyFailure', error, operationInfo)
21 }
22
23 isAdmin (userName) {
24 return this.roomState.ownerGet().then(owner => {
25 if (owner === userName) { return true }
26 return this.roomState.hasInList('adminlist', userName)
27 })
28 }
29
30 hasRemoveChangedCurrentAccess (userName, listName) {
31 return this.roomState.hasInList('userlist', userName).then(hasUser => {
32 if (!hasUser) { return false }
33 return this.isAdmin(userName).then(admin => {
34 if (admin || listName !== 'whitelist') { return false }
35 return this.roomState.whitelistOnlyGet()
36 })
37 }).catch(e => this.consistencyFailure(e, {userName}))
38 }
39
40 hasAddChangedCurrentAccess (userName, listName) {
41 return this.roomState.hasInList('userlist', userName).then(hasUser => {
42 if (!hasUser) { return false }
43 return this.isAdmin(userName)
44 .then(admin => !(admin || listName !== 'blacklist'))
45 }).catch(e => this.consistencyFailure(e, {userName}))
46 }
47
48 getModeChangedCurrentAccess (value) {
49 if (!value) {
50 return []
51 } else {
52 return this.roomState.getCommonUsers()
53 }
54 }
55
56 checkListChanges (author, listName, values, bypassPermissions) {
57 if (listName === 'userlist') {
58 return Promise.reject(new ChatServiceError('notAllowed'))
59 }
60 if (bypassPermissions) { return Promise.resolve() }
61 return this.roomState.ownerGet().then(owner => {
62 if (author === owner) { return Promise.resolve() }
63 if (listName === 'adminlist') {
64 return Promise.reject(new ChatServiceError('notAllowed'))
65 }
66 return this.roomState.hasInList('adminlist', author).then(admin => {
67 if (!admin) {
68 return Promise.reject(new ChatServiceError('notAllowed'))
69 }
70 for (let name of values) {
71 if (name !== owner) { continue }
72 return Promise.reject(new ChatServiceError('notAllowed'))
73 }
74 return Promise.resolve()
75 })
76 })
77 }
78
79 checkModeChange (author, value, bypassPermissions) {
80 return this.isAdmin(author).then(admin => {
81 if (admin || bypassPermissions) { return Promise.resolve() }
82 return Promise.reject(new ChatServiceError('notAllowed'))
83 })
84 }
85
86 checkAcess (userName) {
87 return run(this, function * () {
88 let admin = yield this.isAdmin(userName)
89 if (admin) { return Promise.resolve() }
90 let blacklisted = yield this.roomState.hasInList('blacklist', userName)
91 if (blacklisted) {
92 return Promise.reject(new ChatServiceError('notAllowed'))
93 }
94 let whitelistOnly = yield this.roomState.whitelistOnlyGet()
95 if (!whitelistOnly) { return Promise.resolve() }
96 let whitelisted = yield this.roomState.hasInList('whitelist', userName)
97 if (whitelisted) { return Promise.resolve() }
98 return Promise.reject(new ChatServiceError('notAllowed'))
99 })
100 }
101
102 checkRead (author, bypassPermissions) {
103 if (bypassPermissions) { return Promise.resolve() }
104 return run(this, function * () {
105 let hasAuthor = yield this.roomState.hasInList('userlist', author)
106 if (hasAuthor) { return Promise.resolve() }
107 let admin = yield this.isAdmin(author)
108 if (admin) { return Promise.resolve() }
109 return Promise.reject(new ChatServiceError('notJoined', this.roomName))
110 })
111 }
112
113 checkIsOwner (author, bypassPermissions) {
114 if (bypassPermissions) { return Promise.resolve() }
115 return this.roomState.ownerGet().then(owner => {
116 if (owner === author) { return Promise.resolve() }
117 return Promise.reject(new ChatServiceError('notAllowed'))
118 })
119 }
120
121}
122
123// Implements room messaging state manipulations with the respect to
124// user's permissions.
125class Room {
126
127 constructor (server, roomName) {
128 this.server = server
129 this.roomName = roomName
130 this.listSizeLimit = this.server.roomListSizeLimit
131 let State = this.server.state.RoomState
132 this.roomState = new State(this.server, this.roomName)
133 mixin(this, RoomPermissions, this.roomName,
134 this.roomState, this.server.emit.bind(this.server))
135 }
136
137 initState (state) {
138 return this.roomState.initState(state)
139 }
140
141 removeState () {
142 return this.roomState.removeState()
143 }
144
145 startRemoving () {
146 return this.roomState.startRemoving()
147 }
148
149 getUsers () {
150 return this.roomState.getList('userlist')
151 }
152
153 leave (author) {
154 return this.roomState.hasInList('userlist', author).then(hasAuthor => {
155 if (!hasAuthor) { return Promise.resolve() }
156 return this.roomState.removeFromList('userlist', [author])
157 .then(() => this.roomState.userSeenUpdate(author))
158 })
159 }
160
161 join (author) {
162 return this.checkAcess(author)
163 .then(() => this.roomState.hasInList('userlist', author))
164 .then(hasAuthor => {
165 if (hasAuthor) { return Promise.resolve() }
166 return this.roomState.userSeenUpdate(author)
167 .then(() => this.roomState.addToList('userlist', [author]))
168 })
169 }
170
171 message (author, msg, bypassPermissions) {
172 return Promise.try(() => {
173 if (bypassPermissions) { return Promise.resolve() }
174 return this.roomState.hasInList('userlist', author).then(hasAuthor => {
175 if (hasAuthor) { return Promise.resolve() }
176 return Promise.reject(new ChatServiceError('notJoined', this.roomName))
177 })
178 }).then(() => this.roomState.messageAdd(msg))
179 }
180
181 getList (author, listName, bypassPermissions) {
182 return this.checkRead(author, bypassPermissions)
183 .then(() => this.roomState.getList(listName))
184 }
185
186 getRecentMessages (author, bypassPermissions) {
187 return this.checkRead(author, bypassPermissions)
188 .then(() => this.roomState.messagesGetRecent())
189 }
190
191 getHistoryInfo (author, bypassPermissions) {
192 return this.checkRead(author, bypassPermissions)
193 .then(() => this.roomState.historyInfo())
194 }
195
196 getNotificationsInfo (author, bypassPermissions) {
197 return this.checkRead(author, bypassPermissions)
198 .then(() => Promise.join(
199 this.roomState.userlistUpdatesGet(),
200 this.roomState.accessListsUpdatesGet(),
201 (enableUserlistUpdates, enableAccessListsUpdates) =>
202 ({ enableUserlistUpdates, enableAccessListsUpdates })))
203 }
204
205 getMessages (author, id, limit, bypassPermissions) {
206 return this.checkRead(author, bypassPermissions).then(() => {
207 if (!bypassPermissions) {
208 limit = _.min([ limit, this.server.historyMaxGetMessages ])
209 }
210 return this.roomState.messagesGet(id, limit)
211 })
212 }
213
214 addToList (author, listName, values, bypassPermissions) {
215 return this.checkListChanges(author, listName, values, bypassPermissions)
216 .then(() => this.roomState.addToList(listName, values, this.listSizeLimit))
217 .then(() => Promise.filter(
218 values,
219 val => this.hasAddChangedCurrentAccess(val, listName),
220 { concurrency: asyncLimit }))
221 }
222
223 removeFromList (author, listName, values, bypassPermissions) {
224 return this.checkListChanges(author, listName, values, bypassPermissions)
225 .then(() => this.roomState.removeFromList(listName, values))
226 .then(() => Promise.filter(
227 values,
228 val => this.hasRemoveChangedCurrentAccess(val, listName),
229 { concurrency: asyncLimit }))
230 }
231
232 getMode (author, bypassPermissions) {
233 return this.checkRead(author, bypassPermissions)
234 .then(() => this.roomState.whitelistOnlyGet())
235 }
236
237 getOwner (author, bypassPermissions) {
238 return this.checkRead(author, bypassPermissions)
239 .then(() => this.roomState.ownerGet())
240 }
241
242 changeMode (author, mode, bypassPermissions) {
243 let whitelistOnly = mode
244 return this.checkModeChange(author, mode, bypassPermissions)
245 .then(() => this.roomState.whitelistOnlySet(whitelistOnly))
246 .then(() => this.getModeChangedCurrentAccess(whitelistOnly))
247 .then(usernames => [ usernames, whitelistOnly ])
248 }
249
250 userSeen (author, userName, bypassPermissions) {
251 return this.checkRead(author, bypassPermissions)
252 .then(() => this.roomState.userSeenGet(userName))
253 }
254
255}
256
257module.exports = Room