@@ -9,7 +9,6 @@ import filterByOrg from 'lib/models/plugins/filterByOrg';
9
9
import keys from 'lodash/keys' ;
10
10
import union from 'lodash/union' ;
11
11
import map from 'lodash/map' ;
12
- import find from 'lodash/find' ;
13
12
import isUndefined from 'lodash/isUndefined' ;
14
13
import {
15
14
VIEW_PUBLIC_DASHBOARDS ,
@@ -22,8 +21,62 @@ import { update$dteTimezoneInDB } from 'lib/helpers/update$dteTimezoneInDB';
22
21
23
22
let Organisation ;
24
23
24
+ /**
25
+ * @typedef {object } ExpirationNotifications
26
+ * @property {string } weekBeforeNotificationSent
27
+ * @property {string } expirationNotificationSent
28
+ */
29
+
30
+ /**
31
+ * @typedef {object } UsageStats
32
+ * @property {Date } RUN_DATETIME
33
+ * @property {Boolean } HAS_CHILDREN
34
+ * @property {Number } OWN_COUNT
35
+ * @property {Number } TOTAL_COUNT
36
+ * @property {Number } OWN_ESTIMATED_BYTES
37
+ * @property {Number } TOTAL_ESTIMATED_BYTES
38
+ */
39
+
40
+ /**
41
+ * @typedef {object } Settings
42
+ * @property {Boolean } LOCKOUT_ENABLED
43
+ * @property {Number } LOCKOUT_ATTEMPS
44
+ * @property {Number } LOCKOUT_SECONDS
45
+ * @property {Boolean } PASSWORD_HISTORY_CHECK
46
+ * @property {Number } PASSWORD_HISTORY_TOTAL
47
+ * @property {Number } PASSWORD_MIN_LENGTH
48
+ * @property {Boolean } PASSWORD_REQUIRE_ALPHA
49
+ * @property {Boolean } PASSWORD_REQUIRE_NUMBER
50
+ * @property {Boolean } PASSWORD_USE_CUSTOM_REGEX
51
+ * @property {String } PASSWORD_CUSTOM_REGEX
52
+ * @property {String } PASSWORD_CUSTOM_MESSAGE
53
+ */
54
+
55
+ /**
56
+ * Plain object structure without mongoose model methods
57
+ *
58
+ * @typedef {object } Organisation
59
+ * @property {string } name
60
+ * @property {string } logoPath
61
+ * @property {UploadSchema } logo TODO: define type
62
+ * @property {string } customColors
63
+ * @property {Organisation } parent
64
+ * @property {* } owner TODO: define type
65
+ * @property {Date } expiration
66
+ * @property {ExpirationNotifications } expirationNotifications
67
+ * @property {UsageStats } usageStats
68
+ * @property {Settings } settings
69
+ * @property {string } timezone
70
+ */
71
+
72
+ /** @typedef {module:mongoose.Model<Organisation> } OrganisationModel */
73
+
74
+ export const EMAIL_NOOP = 'EMAIL_NOOP' ;
75
+ export const EMAIL_PROCESSING = 'EMAIL_PROCESSING' ;
76
+ export const EMAIL_SENT = 'EMAIL_SENT' ;
77
+ export const EMAIL_STATUS = [ EMAIL_NOOP , EMAIL_PROCESSING , EMAIL_SENT ] ;
78
+
25
79
async function validateLockoutAttempt ( value ) {
26
- // only validate field if lockout is enabled
27
80
if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
28
81
if ( value < 1 ) {
29
82
throw new Error ( 'A user should be allowed at least one attempt' ) ;
@@ -33,7 +86,6 @@ async function validateLockoutAttempt(value) {
33
86
}
34
87
35
88
async function validateLockoutSeconds ( value ) {
36
- // only validate field if lockout is enabled
37
89
if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
38
90
if ( value < 5 ) {
39
91
throw new Error ( 'Must be at least 5 seconds' ) ;
@@ -43,7 +95,6 @@ async function validateLockoutSeconds(value) {
43
95
}
44
96
45
97
async function validateHistoryTotal ( value ) {
46
- // only validate field if lockout is enabled
47
98
if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
48
99
if ( value < 1 ) {
49
100
throw new Error ( 'At least one password must be stored and checked with this setting enabled' ) ;
@@ -71,12 +122,6 @@ async function validateMinPasswordLength(value) {
71
122
return true ;
72
123
}
73
124
74
- export const EMAIL_NOOP = 'EMAIL_NOOP' ;
75
- export const EMAIL_PROCESSING = 'EMAIL_PROCESSING' ;
76
- export const EMAIL_SENT = 'EMAIL_SENT' ;
77
-
78
- export const EMAIL_STATUS = [ EMAIL_NOOP , EMAIL_PROCESSING , EMAIL_SENT ] ;
79
-
80
125
const schema = new mongoose . Schema ( {
81
126
name : { type : String } ,
82
127
logoPath : { type : String } ,
@@ -105,15 +150,13 @@ const schema = new mongoose.Schema({
105
150
} ,
106
151
settings : {
107
152
LOCKOUT_ENABLED : { type : Boolean , default : true } ,
108
- // number of attempts before lock out
109
153
LOCKOUT_ATTEMPS : {
110
154
type : Number ,
111
155
default : 5 ,
112
156
validate : {
113
157
validator : validateLockoutAttempt ,
114
158
} ,
115
159
} ,
116
- // 30 minute lock out period
117
160
LOCKOUT_SECONDS : {
118
161
type : Number ,
119
162
default : 1800 ,
@@ -176,15 +219,20 @@ schema.plugin(auditRemove, {
176
219
auditName : 'OrganisationAudit'
177
220
} ) ;
178
221
222
+ /**
223
+ * @param req
224
+ * @param res
225
+ * @param next
226
+ */
179
227
schema . statics . readScopeChecks = function runReadScopeChecks ( req , res , next ) {
180
228
// we should always be allowed to read orgs
181
229
return next ( ) ;
182
230
} ;
183
231
184
232
/**
185
233
* Returns an array of org ids that the given client can view limited to the level
186
- * @param {Object } orgIds ids to start with
187
- * @param {Object } totalIds ids found so far
234
+ * @param {Object } stepIds ids to start with
235
+ * @param {Object } cumulativeIds ids found so far
188
236
* @param {Number } level How many levels deep to look for children
189
237
* - default 0 (you can see your orgs)
190
238
* @param {Function } cb Callback to be called with the result
@@ -210,108 +258,102 @@ schema.statics.limitIdsByOrg = function limitIdsByOrg(
210
258
}
211
259
} ;
212
260
213
- schema . post ( 'init' , function handleInit ( ) {
214
- this . _original = this . toObject ( ) ;
215
- } ) ;
216
-
217
- schema . pre ( 'save' , async function preSave ( next ) {
218
- const User = getConnection ( ) . model ( 'User' ) ;
261
+ const createRoles = async ( orgModel ) => {
219
262
const Role = getConnection ( ) . model ( 'Role' ) ;
220
263
221
- // https://github.com/Automattic/mongoose/issues/1474
222
- this . wasNew = this . isNew ;
264
+ await Role . create ( {
265
+ title : 'Observer' ,
266
+ owner_id : orgModel . owner ,
267
+ organisation : orgModel . _id ,
268
+ scopes : [
269
+ VIEW_PUBLIC_DASHBOARDS ,
270
+ VIEW_PUBLIC_VISUALISATIONS ,
271
+ VIEW_PUBLIC_JOURNEYS
272
+ ]
273
+ } ) ;
274
+
275
+ return await Role . create ( {
276
+ title : 'Admin' ,
277
+ owner_id : orgModel . owner ,
278
+ organisation : orgModel . _id ,
279
+ scopes : [ ALL ]
280
+ } ) ;
281
+ } ;
223
282
224
- const organisation = this ;
225
- const objectId = mongoose . Types . ObjectId ;
283
+ const updateOwner = async ( orgModel , User , adminRole ) => {
284
+ const owner = await User . findById ( orgModel . owner ) ;
226
285
227
- if ( ! isUndefined ( this . _oldExpiration ) && this . expiration !== this . _oldExpiration ) {
228
- this . expirationNotifications . expirationNotificationSent = EMAIL_NOOP ;
229
- this . expirationNotifications . weekBeforeNotificationSent = EMAIL_NOOP ;
286
+ if ( ! owner ) {
287
+ return ;
230
288
}
231
289
232
- // https://github.com/Automattic/mongoose/issues/1474
233
- if ( organisation . wasNew ) {
234
- const createAdminRole = await Role . create ( {
235
- title : 'Admin' ,
236
- owner_id : organisation . owner ,
237
- organisation : organisation . _id ,
238
- scopes : [ ALL ]
239
- } ) ;
240
-
241
- const createObserverRole = await Role . create ( {
242
- title : 'Observer' ,
243
- owner_id : organisation . owner ,
244
- organisation : organisation . _id ,
245
- scopes : [
246
- VIEW_PUBLIC_DASHBOARDS ,
247
- VIEW_PUBLIC_VISUALISATIONS ,
248
- VIEW_PUBLIC_JOURNEYS
249
- ]
250
- } ) ;
290
+ owner . organisations = union ( owner . organisations || [ ] , [ orgModel . _id ] ) ;
291
+ owner . organisationSettings = union (
292
+ owner . organisationSettings || [ ] ,
293
+ [ {
294
+ organisation : orgModel . _id ,
295
+ roles : [ adminRole . _id ] ,
296
+ } ]
297
+ ) ;
251
298
252
- const owner = await User . findById ( objectId ( organisation . owner ) ) ;
253
- if ( owner ) {
254
- // add this org to the creator's allowed orgs
255
- const newRoles = [ createAdminRole . _id ] ;
299
+ await owner . save ( ) ;
300
+ } ;
256
301
257
- const existingOrganisationSettings = owner . organisationSettings || [ ] ;
258
- let updatedSettings = existingOrganisationSettings ;
302
+ const createDependencies = async ( orgModel , User ) => {
303
+ const adminRole = await createRoles ( orgModel ) ;
304
+ await updateOwner ( orgModel , User , adminRole ) ;
305
+ } ;
259
306
260
- // check if the user has settings for this org
261
- const existingOS = find ( existingOrganisationSettings , setting =>
262
- setting . organisation . toString ( ) === organisation . _id . toString ( )
263
- ) ;
307
+ const removeDependencies = async ( orgModel ) => {
308
+ const Role = getConnection ( ) . model ( 'Role' ) ;
309
+ const User = getConnection ( ) . model ( 'User' ) ;
264
310
265
- if ( existingOS ) {
266
- const updatedOS = existingOS ;
267
- // if the setting already exists, update it
268
- if ( updatedOS . roles ) {
269
- // union the roles if exist
270
- updatedOS . roles = union ( updatedOS . roles , newRoles ) ;
271
- } else {
272
- // set the new roles if no previous roles exist
273
- updatedOS . roles = newRoles ;
274
- }
275
-
276
- // loop through to create the updated settings array
277
- updatedSettings = map ( updatedSettings , ( os ) => {
278
- if ( os . organisation . toString ( ) === organisation . _id . toString ( ) ) {
279
- // return the updated settings into the array
280
- return updatedOS ;
281
- }
282
- } ) ;
283
- } else {
284
- // insert a new item
285
- updatedSettings . push ( {
286
- organisation : organisation . _id ,
287
- roles : newRoles
288
- } ) ;
311
+ await Role . deleteMany ( { organisation : [ orgModel . _id ] } ) ;
312
+ await User . updateMany (
313
+ { } ,
314
+ {
315
+ $pull : {
316
+ organisations : orgModel . _id ,
317
+ organisationSettings : { organisation : orgModel . _id } ,
289
318
}
319
+ }
320
+ ) ;
321
+ } ;
290
322
291
- // update the settings on the owner
292
- owner . organisations = union ( owner . organisations , [ organisation . _id ] ) ;
293
- owner . organisationSettings = updatedSettings ;
294
323
295
- await owner . save ( ) ;
296
- }
324
+ schema . post ( 'init' , function handleInit ( ) {
325
+ this . _original = this . toObject ( ) ;
326
+ } ) ;
327
+
328
+ schema . pre ( 'save' , async function preSave ( next ) {
329
+ const User = getConnection ( ) . model ( 'User' ) ;
330
+
331
+ if ( ! isUndefined ( this . _oldExpiration ) && this . expiration !== this . _oldExpiration ) {
332
+ this . expirationNotifications . expirationNotificationSent = EMAIL_NOOP ;
333
+ this . expirationNotifications . weekBeforeNotificationSent = EMAIL_NOOP ;
334
+ }
297
335
298
- await Promise . all ( [ createAdminRole , createObserverRole ] ) ;
336
+ if ( ! isUndefined ( this . _oldTimezone ) && this . timezone !== this . _oldTimezone ) {
337
+ await update$dteTimezoneInDB ( this . _id , this . timezone ) ;
299
338
}
300
339
301
- // Update users settings
302
340
await User . updateMany (
303
- { ownerOrganisation : organisation . _id } ,
341
+ { ownerOrganisation : this . _id } ,
304
342
{ ownerOrganisationSettings : this . settings } ,
305
343
{ runValidators : false } ,
306
344
) ;
307
345
308
- // Update timezone offset of queries
309
- if ( ! isUndefined ( this . _oldTimezone ) && this . timezone !== this . _oldTimezone ) {
310
- await update$dteTimezoneInDB ( organisation . _id , this . timezone ) ;
346
+ if ( this . isNew ) {
347
+ await createDependencies ( this , User ) ;
311
348
}
312
349
313
350
next ( ) ;
314
351
} ) ;
315
352
353
+ schema . post ( 'remove' , async ( orgModel , next ) => {
354
+ await removeDependencies ( orgModel ) ;
355
+ next ( ) ;
356
+ } ) ;
357
+
316
358
Organisation = getConnection ( ) . model ( 'Organisation' , schema , 'organisations' ) ;
317
359
export default Organisation ;
0 commit comments