@@ -9,7 +9,6 @@ import filterByOrg from 'lib/models/plugins/filterByOrg';
99import keys from 'lodash/keys' ;
1010import union from 'lodash/union' ;
1111import map from 'lodash/map' ;
12- import find from 'lodash/find' ;
1312import isUndefined from 'lodash/isUndefined' ;
1413import {
1514 VIEW_PUBLIC_DASHBOARDS ,
@@ -22,8 +21,62 @@ import { update$dteTimezoneInDB } from 'lib/helpers/update$dteTimezoneInDB';
2221
2322let Organisation ;
2423
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+
2579async function validateLockoutAttempt ( value ) {
26- // only validate field if lockout is enabled
2780 if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
2881 if ( value < 1 ) {
2982 throw new Error ( 'A user should be allowed at least one attempt' ) ;
@@ -33,7 +86,6 @@ async function validateLockoutAttempt(value) {
3386}
3487
3588async function validateLockoutSeconds ( value ) {
36- // only validate field if lockout is enabled
3789 if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
3890 if ( value < 5 ) {
3991 throw new Error ( 'Must be at least 5 seconds' ) ;
@@ -43,7 +95,6 @@ async function validateLockoutSeconds(value) {
4395}
4496
4597async function validateHistoryTotal ( value ) {
46- // only validate field if lockout is enabled
4798 if ( this . settings && this . settings . LOCKOUT_ENABLED ) {
4899 if ( value < 1 ) {
49100 throw new Error ( 'At least one password must be stored and checked with this setting enabled' ) ;
@@ -71,12 +122,6 @@ async function validateMinPasswordLength(value) {
71122 return true ;
72123}
73124
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-
80125const schema = new mongoose . Schema ( {
81126 name : { type : String } ,
82127 logoPath : { type : String } ,
@@ -105,15 +150,13 @@ const schema = new mongoose.Schema({
105150 } ,
106151 settings : {
107152 LOCKOUT_ENABLED : { type : Boolean , default : true } ,
108- // number of attempts before lock out
109153 LOCKOUT_ATTEMPS : {
110154 type : Number ,
111155 default : 5 ,
112156 validate : {
113157 validator : validateLockoutAttempt ,
114158 } ,
115159 } ,
116- // 30 minute lock out period
117160 LOCKOUT_SECONDS : {
118161 type : Number ,
119162 default : 1800 ,
@@ -176,15 +219,20 @@ schema.plugin(auditRemove, {
176219 auditName : 'OrganisationAudit'
177220} ) ;
178221
222+ /**
223+ * @param req
224+ * @param res
225+ * @param next
226+ */
179227schema . statics . readScopeChecks = function runReadScopeChecks ( req , res , next ) {
180228 // we should always be allowed to read orgs
181229 return next ( ) ;
182230} ;
183231
184232/**
185233 * 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
188236 * @param {Number } level How many levels deep to look for children
189237 * - default 0 (you can see your orgs)
190238 * @param {Function } cb Callback to be called with the result
@@ -210,108 +258,102 @@ schema.statics.limitIdsByOrg = function limitIdsByOrg(
210258 }
211259} ;
212260
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 ) => {
219262 const Role = getConnection ( ) . model ( 'Role' ) ;
220263
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+ } ;
223282
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 ) ;
226285
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 ;
230288 }
231289
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+ ) ;
251298
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+ } ;
256301
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+ } ;
259306
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' ) ;
264310
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 } ,
289318 }
319+ }
320+ ) ;
321+ } ;
290322
291- // update the settings on the owner
292- owner . organisations = union ( owner . organisations , [ organisation . _id ] ) ;
293- owner . organisationSettings = updatedSettings ;
294323
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+ }
297335
298- await Promise . all ( [ createAdminRole , createObserverRole ] ) ;
336+ if ( ! isUndefined ( this . _oldTimezone ) && this . timezone !== this . _oldTimezone ) {
337+ await update$dteTimezoneInDB ( this . _id , this . timezone ) ;
299338 }
300339
301- // Update users settings
302340 await User . updateMany (
303- { ownerOrganisation : organisation . _id } ,
341+ { ownerOrganisation : this . _id } ,
304342 { ownerOrganisationSettings : this . settings } ,
305343 { runValidators : false } ,
306344 ) ;
307345
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 ) ;
311348 }
312349
313350 next ( ) ;
314351} ) ;
315352
353+ schema . post ( 'remove' , async ( orgModel , next ) => {
354+ await removeDependencies ( orgModel ) ;
355+ next ( ) ;
356+ } ) ;
357+
316358Organisation = getConnection ( ) . model ( 'Organisation' , schema , 'organisations' ) ;
317359export default Organisation ;
0 commit comments