diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee
index bd4bf9750..d55dcadbe 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..0372d073c 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', 'savedEmployerFilterAlerts'
+]
+
 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 5e67c58c3..a450951b0 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', 'savedEmployerFilterAlerts'
-  ]
 
   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