From 19d59ac8ff98e92e0f58ea50e50fbdc29b782367 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken Date: Tue, 22 Jul 2014 20:07:00 +0200 Subject: [PATCH] Refactored Users and Level Sessions to have private properties: --- server/commons/Handler.coffee | 16 ++++++++++++++++ server/levels/sessions/LevelSession.coffee | 6 ++++++ .../sessions/level_session_handler.coffee | 5 ----- server/users/User.coffee | 12 ++++++++++++ server/users/user_handler.coffee | 17 +++-------------- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee index 9dfca705b..9c2466cae 100644 --- a/server/commons/Handler.coffee +++ b/server/commons/Handler.coffee @@ -13,12 +13,20 @@ FETCH_LIMIT = 200 module.exports = class Handler # subclasses should override these properties modelClass: null + privateProperties: [] editableProperties: [] postEditableProperties: [] jsonSchema: {} waterfallFunctions: [] allowedMethods: ['GET', 'POST', 'PUT', 'PATCH'] + constructor: -> + # TODO The second 'or' is for backward compatibility only is for backward compatibility only + @privateProperties = @modelClass.privateProperties or @privateProperties or [] + @editableProperties = @modelClass.editableProperties or @editableProperties or [] + @postEditableProperties = @modelClass.postEditableProperties or @postEditableProperties or [] + @jsonSchema = @modelClass.jsonSchema or @jsonSchema or {} + # subclasses should override these methods hasAccess: (req) -> true hasAccessToDocument: (req, document, method=null) -> @@ -435,3 +443,11 @@ module.exports = class Handler delete: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, 'DELETE not allowed.' head: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, 'HEAD not allowed.' + + # This is not a Mongoose user + projectionForUser: (req, model, ownerID) -> + return {} if 'privateProperties' not of model or req.user._id + '' is ownerID + '' or req.user.isAdmin() + projection = {} + projection[field] = 0 for field in model.privateProperties + projection + diff --git a/server/levels/sessions/LevelSession.coffee b/server/levels/sessions/LevelSession.coffee index 4873e40a5..b8f0a58dc 100644 --- a/server/levels/sessions/LevelSession.coffee +++ b/server/levels/sessions/LevelSession.coffee @@ -24,4 +24,10 @@ LevelSessionSchema.pre 'save', (next) -> @set('changed', new Date()) next() +LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed'] +LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state', + 'levelName', 'creatorName', 'levelID', 'screenshot', + 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime'] +LevelSessionSchema.statics.jsonSchema = jsonschema + module.exports = LevelSession = mongoose.model('level.session', LevelSessionSchema) diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee index b1179acf8..678b33e80 100644 --- a/server/levels/sessions/level_session_handler.coffee +++ b/server/levels/sessions/level_session_handler.coffee @@ -6,11 +6,6 @@ TIMEOUT = 1000 * 30 # no activity for 30 seconds means it's not active class LevelSessionHandler extends Handler modelClass: LevelSession - editableProperties: ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state', - 'levelName', 'creatorName', 'levelID', 'screenshot', - 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime'] - privateProperties: ['code', 'submittedCode', 'unsubscribed'] - jsonSchema: require '../../../app/schemas/models/level_session' getByRelationship: (req, res, args...) -> return @getActiveSessions req, res if args.length is 2 and args[1] is 'active' diff --git a/server/users/User.coffee b/server/users/User.coffee index 13f4c11fb..630044850 100644 --- a/server/users/User.coffee +++ b/server/users/User.coffee @@ -158,6 +158,18 @@ UserSchema.statics.hashPassword = (password) -> shasum.update(salt + password) shasum.digest('hex') +UserSchema.statics.privateProperties = [ + 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID', + 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement' +] +UserSchema.statics.jsonSchema = jsonschema +UserSchema.statics.editableProperties = [ + 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume', + 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails', + 'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage', + 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile' +] + UserSchema.plugin plugins.NamedPlugin module.exports = User = mongoose.model('User', UserSchema) diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index fbd13ec19..d36f6c1dc 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -14,37 +14,26 @@ EarnedAchievement = require '../achievements/EarnedAchievement' UserRemark = require './remarks/UserRemark' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset'] -privateProperties = [ - 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID', - 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement' -] candidateProperties = [ 'jobProfile', 'jobProfileApproved', 'jobProfileNotes' ] UserHandler = class UserHandler extends Handler modelClass: User - jsonSchema: schema - editableProperties: [ - 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume', - 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails', - 'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage', - 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile' - ] getEditableProperties: (req, document) -> props = super req, document props.push 'permissions' unless config.isProduction props.push 'jobProfileApproved', 'jobProfileNotes','jobProfileApprovedDate' if req.user.isAdmin() # Admins naturally edit these - props.push privateProperties... if req.user.isAdmin() # Admins are mad with power + props.push @privateProperties... if req.user.isAdmin() # Admins are mad with power props - formatEntity: (req, document) -> + formatEntity: (req, document) => return null unless document? obj = document.toObject() delete obj[prop] for prop in serverProperties includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id)) - delete obj[prop] for prop in privateProperties unless includePrivates + delete obj[prop] for prop in @privateProperties unless includePrivates includeCandidate = includePrivates or (obj.jobProfile?.active and req.user and ('employer' in (req.user.get('permissions') ? [])) and @employerCanViewCandidate req.user, obj) delete obj[prop] for prop in candidateProperties unless includeCandidate return obj