2015-12-16 20:09:22 -05:00
# Middleware for both authentication and authorization
errors = require ' ../commons/errors '
2016-03-03 17:22:50 -05:00
wrap = require ' co-express '
Promise = require ' bluebird '
2016-02-25 18:24:16 -05:00
parse = require ' ../commons/parse '
request = require ' request '
2016-04-06 13:56:06 -04:00
User = require ' ../models/User '
2016-02-25 18:24:16 -05:00
utils = require ' ../lib/utils '
2016-03-03 17:22:50 -05:00
mongoose = require ' mongoose '
2016-04-11 19:51:51 -04:00
authentication = require ' passport '
sendwithus = require ' ../sendwithus '
LevelSession = require ' ../models/LevelSession '
2015-12-16 20:09:22 -05:00
2016-02-25 18:24:16 -05:00
module.exports =
2015-12-16 20:09:22 -05:00
checkDocumentPermissions: (req, res, next) ->
return next ( ) if req . user ? . isAdmin ( )
if not req . doc . hasPermissionsForMethod ( req . user , req . method )
if req . user
return next new errors . Forbidden ( ' You do not have permissions necessary. ' )
return next new errors . Unauthorized ( ' You must be logged in. ' )
next ( )
checkLoggedIn: ->
return (req, res, next) ->
2016-03-30 19:20:37 -04:00
if ( not req . user ) or ( req . user . isAnonymous ( ) )
2015-12-16 20:09:22 -05:00
return next new errors . Unauthorized ( ' You must be logged in. ' )
next ( )
checkHasPermission: (permissions) ->
if _ . isString ( permissions )
permissions = [ permissions ]
return (req, res, next) ->
if not req . user
return next new errors . Unauthorized ( ' You must be logged in. ' )
if not _ . size ( _ . intersection ( req . user . get ( ' permissions ' ) , permissions ) )
return next new errors . Forbidden ( ' You do not have permissions necessary. ' )
next ( )
2016-04-11 19:51:51 -04:00
whoAmI: wrap (req, res) ->
if not req . user
user = User . makeNew ( req )
yield user . save ( )
req.logInAsync = Promise . promisify ( req . logIn )
yield req . logInAsync ( user )
if req . query . callback
res . jsonp ( req . user . toObject ( { req , publicOnly: true } ) )
else
res . send ( req . user . toObject ( { req , publicOnly: false } ) )
res . end ( )
afterLogin: wrap (req, res, next) ->
activity = req . user . trackActivity ' login ' , 1
yield req . user . update { activity: activity }
res . status ( 200 ) . send ( req . user . toObject ( { req: req } ) )
2015-12-16 20:09:22 -05:00
2016-04-11 19:51:51 -04:00
loginByGPlus: wrap (req, res, next) ->
2016-02-25 18:24:16 -05:00
gpID = req . body . gplusID
gpAT = req . body . gplusAccessToken
throw new errors . UnprocessableEntity ( ' gplusID and gplusAccessToken required. ' ) unless gpID and gpAT
url = " https://www.googleapis.com/oauth2/v2/userinfo?access_token= #{ gpAT } "
[ googleRes , body ] = yield request . getAsync ( url , { json: true } )
idsMatch = gpID is body . id
throw new errors . UnprocessableEntity ( ' Invalid G+ Access Token. ' ) unless idsMatch
user = yield User . findOne ( { gplusID: gpID } )
throw new errors . NotFound ( ' No user with that G+ ID ' ) unless user
req.logInAsync = Promise . promisify ( req . logIn )
yield req . logInAsync ( user )
2016-04-11 19:51:51 -04:00
next ( )
2016-02-25 18:24:16 -05:00
2016-04-11 19:51:51 -04:00
loginByFacebook: wrap (req, res, next) ->
2016-02-25 18:24:16 -05:00
fbID = req . body . facebookID
fbAT = req . body . facebookAccessToken
throw new errors . UnprocessableEntity ( ' facebookID and facebookAccessToken required. ' ) unless fbID and fbAT
url = " https://graph.facebook.com/me?access_token= #{ fbAT } "
[ facebookRes , body ] = yield request . getAsync ( url , { json: true } )
idsMatch = fbID is body . id
throw new errors . UnprocessableEntity ( ' Invalid Facebook Access Token. ' ) unless idsMatch
user = yield User . findOne ( { facebookID: fbID } )
throw new errors . NotFound ( ' No user with that Facebook ID ' ) unless user
req.logInAsync = Promise . promisify ( req . logIn )
yield req . logInAsync ( user )
2016-04-11 19:51:51 -04:00
next ( )
2016-02-25 18:24:16 -05:00
2016-03-03 17:22:50 -05:00
spy: wrap (req, res) ->
throw new errors . Unauthorized ( ' You must be logged in to enter espionage mode ' ) unless req . user
throw new errors . Forbidden ( ' You must be an admin to enter espionage mode ' ) unless req . user . isAdmin ( )
user = req . body . user
throw new errors . UnprocessableEntity ( ' Specify an id, username or email to espionage. ' ) unless user
if utils . isID ( user )
query = { _id: mongoose . Types . ObjectId ( user ) }
else
user = user . toLowerCase ( )
query = $or: [ { nameLower: user } , { emailLower: user } ]
user = yield User . findOne ( query )
amActually = req . user
throw new errors . NotFound ( ) unless user
req.loginAsync = Promise . promisify ( req . login )
yield req . loginAsync user
req.session.amActually = amActually . id
res . status ( 200 ) . send ( user . toObject ( { req: req } ) )
stopSpying: wrap (req, res) ->
throw new errors . Unauthorized ( ' You must be logged in to leave espionage mode ' ) unless req . user
throw new errors . Forbidden ( ' You must be in espionage mode to leave it ' ) unless req . session . amActually
user = yield User . findById ( req . session . amActually )
delete req . session . amActually
throw new errors . NotFound ( ) unless user
req.loginAsync = Promise . promisify ( req . login )
yield req . loginAsync user
res . status ( 200 ) . send ( user . toObject ( { req: req } ) )
2016-04-11 19:51:51 -04:00
logout: (req, res) ->
req . logout ( )
res . send ( { } )
reset: wrap (req, res) ->
unless req . body . email
throw new errors . UnprocessableEntity ( ' Need an email specified. ' , { property: ' email ' } )
user = yield User . findOne ( { emailLower: req . body . email . toLowerCase ( ) } )
if not user
throw new errors . NotFound ( ' not found ' , { property: ' email ' } )
user . set ( ' passwordReset ' , utils . getCodeCamel ( ) )
emailContent = " <h3>Your temporary password: <b> #{ user . get ( ' passwordReset ' ) } </b></h3> "
emailContent += " <p>Reset your password at <a href= \" http://codecombat.com/account/settings \" >http://codecombat.com/account/settings</a></p> "
emailContent += " <p>Your old password cannot be retrieved.</p> "
yield user . save ( )
context =
email_id: sendwithus . templates . generic_email
recipient:
address: req . body . email
email_data:
subject: ' CodeCombat Recovery Password '
title: ' '
content: emailContent
sendwithus.api.sendAsync = Promise . promisify ( sendwithus . api . send )
yield sendwithus . api . sendAsync ( context )
res . end ( )
unsubscribe: wrap (req, res) ->
email = req . query . email
unless email
throw new errors . UnprocessableEntity ' No email provided to unsubscribe. '
email = decodeURIComponent ( email )
if req . query . session
# Unsubscribe from just one session's notifications instead.
session = yield LevelSession . findOne ( { _id: req . query . session } )
if not session
throw new errors . NotFound " Level session not found "
session . set ' unsubscribed ' , true
yield session . save ( )
res . send " Unsubscribed #{ email } from CodeCombat emails for #{ session . get ( ' levelName ' ) } #{ session . get ( ' team ' ) } ladder updates. Sorry to see you go! <p><a href= ' /play/ladder/ #{ session . levelID } # my-matches ' >Ladder preferences</a></p> "
res . end ( )
return
user = yield User . findOne ( { emailLower: email . toLowerCase ( ) } )
if not user
throw new errors . NotFound " No user found with email ' #{ email } ' "
emails = _ . clone ( user . get ( ' emails ' ) ) or { }
msg = ' '
if req . query . recruitNotes
emails . recruitNotes ? = { }
emails.recruitNotes.enabled = false
msg = " Unsubscribed #{ email } from recruiting emails. "
else if req . query . employerNotes
emails . employerNotes ? = { }
emails.employerNotes.enabled = false
msg = " Unsubscribed #{ email } from employer emails. "
else
msg = " Unsubscribed #{ email } from all CodeCombat emails. Sorry to see you go! "
emailSettings.enabled = false for emailSettings in _ . values ( emails )
emails . generalNews ? = { }
emails.generalNews.enabled = false
emails . anyNotes ? = { }
emails.anyNotes.enabled = false
yield user . update { $set: { emails: emails } }
res . send msg + ' <p><a href= " /account/settings " >Account settings</a></p> '
res . end ( )
name: wrap (req, res) ->
if not req . params . name
throw new errors . UnprocessableEntity ' No name provided. '
originalName = req . params . name
User.unconflictNameAsync = Promise . promisify ( User . unconflictName )
name = yield User . unconflictNameAsync originalName
response = name: name
if originalName is name
res . send 200 , response
else
throw new errors . Conflict ( ' Name is taken ' , response )