2014-06-30 22:16:26 -04:00
authentication = require ' passport '
2014-01-03 13:32:13 -05:00
LocalStrategy = require ( ' passport-local ' ) . Strategy
2014-06-30 22:16:26 -04:00
User = require ' ../users/User '
UserHandler = require ' ../users/user_handler '
2014-03-11 00:30:46 -04:00
LevelSession = require ' ../levels/sessions/LevelSession '
2014-01-22 15:46:44 -05:00
config = require ' ../../server_config '
2014-01-22 17:57:41 -05:00
errors = require ' ../commons/errors '
2014-02-24 14:12:52 -05:00
languages = require ' ../routes/languages '
2014-12-11 23:06:03 -05:00
sendwithus = require ' ../sendwithus '
2015-07-27 14:35:20 -04:00
log = require ' winston '
2014-01-03 13:32:13 -05:00
2014-02-04 16:29:13 -05:00
module.exports.setup = (app) ->
2014-02-04 17:08:20 -05:00
authentication . serializeUser ( (user, done) -> done ( null , user . _id ) )
authentication . deserializeUser ( (id, done) ->
2014-01-03 13:32:13 -05:00
User . findById ( id , (err, user) -> done ( err , user ) ) )
2014-02-04 17:08:20 -05:00
authentication . use ( new LocalStrategy (
2014-01-03 13:32:13 -05:00
(username, password, done) ->
2014-11-29 13:34:04 -05:00
2014-11-20 18:54:15 -05:00
# kind of a hacky way to make it possible for iPads to 'log in' with their unique device id
2014-11-20 20:03:24 -05:00
if username . length is 36 and ' @ ' not in username # must be an identifier for vendor
q = { iosIdentifierForVendor: username }
2014-11-20 18:54:15 -05:00
else
q = { emailLower: username . toLowerCase ( ) }
2014-11-29 13:34:04 -05:00
2014-11-20 18:54:15 -05:00
User . findOne ( q ) . exec ( (err, user) ->
2014-01-03 13:32:13 -05:00
return done ( err ) if err
2014-06-30 22:16:26 -04:00
return done ( null , false , { message: ' not found ' , property: ' email ' } ) if not user
2014-01-03 13:32:13 -05:00
passwordReset = ( user . get ( ' passwordReset ' ) or ' ' ) . toLowerCase ( )
if passwordReset and password . toLowerCase ( ) is passwordReset
User . update { _id: user . get ( ' _id ' ) } , { passwordReset: ' ' } , { } , ->
return done ( null , user )
2014-03-11 00:30:46 -04:00
2014-01-03 13:32:13 -05:00
hash = User . hashPassword ( password )
unless user . get ( ' passwordHash ' ) is hash
2015-01-08 14:27:37 -05:00
return done ( null , false , { message: ' is wrong ' , property: ' password ' } )
2014-01-03 13:32:13 -05:00
return done ( null , user )
)
) )
2014-06-30 22:16:26 -04:00
2014-02-26 17:14:43 -05:00
app . post ' /auth/spy ' , (req, res, next) ->
if req ? . user ? . isAdmin ( )
2014-08-30 20:09:57 -04:00
target = req . body . nameOrEmailLower
return errors . badInput res , ' Specify a username or email to espionage. ' unless target
query = $or: [ { nameLower: target } , { emailLower: target } ]
2014-02-26 17:14:43 -05:00
User . findOne query , (err, user) ->
2014-06-30 22:16:26 -04:00
if err ? then return errors . serverError res , ' There was an error finding the specified user '
unless user then return errors . badInput res , ' The specified user couldn \' t be found '
2014-02-26 17:14:43 -05:00
req . logIn user , (err) ->
2014-08-30 20:09:57 -04:00
if err ? then return errors . serverError res , ' There was an error logging in with the specified user '
2014-02-26 17:14:43 -05:00
res . send ( UserHandler . formatEntity ( req , user ) )
return res . end ( )
else
2014-06-30 22:16:26 -04:00
return errors . unauthorized res , ' You must be an admin to enter espionage mode '
2014-03-11 00:30:46 -04:00
2014-01-03 13:32:13 -05:00
app . post ( ' /auth/login ' , (req, res, next) ->
2014-02-04 17:08:20 -05:00
authentication . authenticate ( ' local ' , (err, user, info) ->
2014-01-03 13:32:13 -05:00
return next ( err ) if err
if not user
2014-06-30 22:16:26 -04:00
return errors . unauthorized ( res , [ { message: info . message , property: info . property } ] )
2014-01-03 13:32:13 -05:00
req . logIn ( user , (err) ->
return next ( err ) if ( err )
2014-06-10 19:30:07 -04:00
activity = req . user . trackActivity ' login ' , 1
user . update { activity: activity } , (err) ->
return next ( err ) if ( err )
res . send ( UserHandler . formatEntity ( req , req . user ) )
return res . end ( )
2014-01-03 13:32:13 -05:00
)
) ( req , res , next )
)
app . get ( ' /auth/whoami ' , (req, res) ->
2014-02-24 14:12:52 -05:00
if req . user
sendSelf ( req , res )
else
2014-04-02 16:12:24 -04:00
user = makeNewUser ( req )
2014-02-24 14:12:52 -05:00
makeNext = (req, res) -> -> sendSelf ( req , res )
next = makeNext ( req , res )
loginUser ( req , res , user , false , next )
)
sendSelf = (req, res) ->
res . setHeader ( ' Content-Type ' , ' text/json ' )
2014-09-22 17:56:02 -04:00
if req . query . callback
res . jsonp UserHandler . formatEntity ( req , req . user , true )
else
res . send UserHandler . formatEntity ( req , req . user , false )
2014-01-03 13:32:13 -05:00
res . end ( )
2014-02-24 14:12:52 -05:00
2014-01-03 13:32:13 -05:00
app . post ( ' /auth/logout ' , (req, res) ->
req . logout ( )
res . end ( )
)
2014-01-14 17:13:47 -05:00
2014-01-03 13:32:13 -05:00
app . post ( ' /auth/reset ' , (req, res) ->
unless req . body . email
2014-06-30 22:16:26 -04:00
return errors . badInput ( res , [ { message: ' Need an email specified. ' , property: ' email ' } ] )
2014-01-14 17:13:47 -05:00
2014-06-30 22:16:26 -04:00
User . findOne ( { emailLower: req . body . email . toLowerCase ( ) } ) . exec ( (err, user) ->
2014-01-03 13:32:13 -05:00
if not user
2015-06-02 16:34:01 -04:00
return errors . notFound ( res , [ { message: ' not found ' , property: ' email ' } ] )
2014-03-11 00:30:46 -04:00
2014-06-30 22:16:26 -04:00
user . set ( ' passwordReset ' , Math . random ( ) . toString ( 36 ) . slice ( 2 , 7 ) . toUpperCase ( ) )
2014-01-03 13:32:13 -05:00
user . save (err) =>
2014-01-14 17:13:47 -05:00
return errors . serverError ( res ) if err
2014-12-11 23:06:03 -05:00
unless config . unittest
context =
email_id: sendwithus . templates . generic_email
recipient:
address: req . body . email
email_data:
subject: ' CodeCombat Recovery Password '
title: ' Recovery Password '
content: " <p>Your CodeCombat recovery password for email #{ req . body . email } is: #{ user . get ( ' passwordReset ' ) } </p><p>Log in at <a href= \" http://codecombat.com/account/settings \" >http://codecombat.com/account/settings</a> and change it.</p><p>Hope this helps!</p> "
sendwithus . api . send context , (err, result) ->
if err
console . error " Error sending password reset email: #{ err . message or err } "
2014-01-14 17:13:47 -05:00
return errors . serverError ( res ) if err
2014-01-03 13:32:13 -05:00
else
return res . end ( )
2014-01-06 15:14:12 -05:00
else
2014-05-09 19:33:06 -04:00
console . log ' password is ' , user . get ( ' passwordReset ' )
2014-02-02 18:02:47 -05:00
res . send user . get ( ' passwordReset ' )
2014-01-06 15:14:12 -05:00
return res . end ( )
2014-01-03 13:32:13 -05:00
)
)
2014-03-11 00:30:46 -04:00
2014-08-30 20:09:57 -04:00
app . get ' /auth/unsubscribe ' , (req, res) ->
2014-07-16 22:46:06 -04:00
req.query.email = decodeURIComponent ( req . query . email )
2014-01-17 13:47:42 -05:00
email = req . query . email
unless req . query . email
return errors . badInput res , ' No email provided to unsubscribe. '
2014-03-11 00:30:46 -04:00
if req . query . session
# Unsubscribe from just one session's notifications instead.
return LevelSession . findOne ( { _id: req . query . session } ) . exec (err, session) ->
return errors . serverError res , ' Could not unsubscribe: # {req.query.session}, # {req.query.email}: # {err} ' if err
session . set ' unsubscribed ' , true
session . save (err) ->
return errors . serverError res , ' Database failure. ' if err
2014-12-17 22:38:11 -05:00
res . send " Unsubscribed #{ req . query . 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> "
2014-03-11 00:30:46 -04:00
res . end ( )
2014-08-30 20:09:57 -04:00
2014-06-30 22:16:26 -04:00
User . findOne ( { emailLower: req . query . email . toLowerCase ( ) } ) . exec (err, user) ->
2014-01-17 13:47:42 -05:00
if not user
return errors . notFound res , " No user found with email ' #{ req . query . email } ' "
2014-04-21 19:15:23 -04:00
emails = _ . clone ( user . get ( ' emails ' ) ) or { }
2014-04-22 22:27:39 -04:00
msg = ' '
2014-06-10 19:30:07 -04:00
2014-04-22 22:27:39 -04:00
if req . query . recruitNotes
emails . recruitNotes ? = { }
emails.recruitNotes.enabled = false
msg = " Unsubscribed #{ req . query . email } from recruiting emails. "
2014-07-16 19:37:06 -04:00
else if req . query . employerNotes
emails . employerNotes ? = { }
emails.employerNotes.enabled = false
2014-08-30 20:09:57 -04:00
2014-07-16 19:37:06 -04:00
msg = " Unsubscribed #{ req . query . email } from employer emails. "
2014-04-22 22:27:39 -04:00
else
msg = " Unsubscribed #{ req . query . 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
2014-06-10 19:30:07 -04:00
2014-04-22 22:27:39 -04:00
user . update { $set: { emails: emails } } , { } , =>
2014-01-17 13:47:42 -05:00
return errors . serverError res , ' Database failure. ' if err
2014-06-30 22:16:26 -04:00
res . send msg + ' <p><a href= " /account/settings " >Account settings</a></p> '
2014-01-17 13:47:42 -05:00
res . end ( )
2014-01-03 13:32:13 -05:00
2014-07-14 14:05:03 -04:00
app . get ' /auth/name* ' , (req, res) ->
2014-07-10 14:50:16 -04:00
parts = req . path . split ' / '
2014-07-14 14:05:03 -04:00
originalName = decodeURI parts [ 3 ]
return errors . badInput res , ' No name provided. ' unless parts . length > 3 and originalName ? and originalName isnt ' '
2014-07-10 14:50:16 -04:00
return errors . notFound res if parts . length isnt 4
User . unconflictName originalName , (err, name) ->
return errors . serverError res , err if err
response = name: name
if originalName is name
res . send 200 , response
else
errors . conflict res , response
2014-04-02 16:12:24 -04:00
module.exports.loginUser = loginUser = (req, res, user, send=true, next=null) ->
user . save ( (err) ->
2014-07-10 04:07:36 -04:00
return errors . serverError res , err if err ?
2014-04-02 16:12:24 -04:00
req . logIn ( user , (err) ->
2014-07-10 04:07:36 -04:00
return errors . serverError res , err if err ?
2014-07-10 14:50:16 -04:00
return res . send ( user ) and res . end ( ) if send
2014-04-02 16:12:24 -04:00
next ( ) if next
)
)
2015-12-09 17:27:10 -05:00
module.exports.idCounter = 0
2014-04-02 16:12:24 -04:00
module.exports.makeNewUser = makeNewUser = (req) ->
2014-06-30 22:16:26 -04:00
user = new User ( { anonymous: true } )
2015-12-09 17:27:10 -05:00
if global . testing
# allows tests some control over user id creation
newID = _ . pad ( ( module . exports . idCounter ++ ) . toString ( 16 ) , 24 , ' 0 ' )
user . set ( ' _id ' , newID )
2015-07-27 14:35:20 -04:00
user . set ' testGroupNumber ' , Math . floor ( Math . random ( ) * 256 ) # also in app/core/auth
2014-11-25 19:20:41 -05:00
lang = languages . languageCodeFromAcceptedLanguages req . acceptedLanguages
user . set ' preferredLanguage ' , lang if lang [ . . . 2 ] isnt ' en '
2015-09-18 11:38:12 -04:00
user . set ' preferredLanguage ' , ' pt-BR ' if not user . get ( ' preferredLanguage ' ) and /br\.codecombat\.com/ . test ( req . get ( ' host ' ) )
user . set ' preferredLanguage ' , ' zh-HANS ' if not user . get ( ' preferredLanguage ' ) and /cn\.codecombat\.com/ . test ( req . get ( ' host ' ) )
2015-08-08 15:25:56 -04:00
user . set ' lastIP ' , ( req . headers [ ' x-forwarded-for ' ] or req . connection . remoteAddress ) ? . split ( /,? / ) [ 0 ]
2015-10-09 11:05:34 -04:00
user . set ' country ' , req . country if req . country
#log.info "making new user #{user.get('_id')} with language #{user.get('preferredLanguage')} of #{req.acceptedLanguages} and country #{req.country} on #{if config.tokyo then 'Tokyo' else (if config.saoPaulo then 'Brazil' else 'US')} server and lastIP #{user.get('lastIP')}."
2015-03-23 19:38:12 -04:00
user