1 | 'use strict'
|
2 |
|
3 | const ChatServiceError = require('./ChatServiceError')
|
4 | const Promise = require('bluebird')
|
5 | const _ = require('lodash')
|
6 | const { mixin } = require('es6-mixin')
|
7 | const { asyncLimit, run } = require('./utils')
|
8 |
|
9 | class 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 |
|
124 |
|
125 | class 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 |
|
257 | module.exports = Room
|