Pull in changes from private branch, including many bug fixes and -

drumrole - functioning facebook auth!!
This commit is contained in:
Tyler Renelle
2012-09-15 15:06:51 -04:00
parent 10c5fecb75
commit bde52a01a9
11 changed files with 141 additions and 165 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ public/gen
node_modules node_modules
#lib/ #lib/
*.swp *.swp
.idea*

View File

@@ -1,2 +1,5 @@
compile: compile:
./node_modules/coffee-script/bin/coffee -bw -o ./lib -c ./src ./node_modules/coffee-script/bin/coffee -bw -o ./lib -c ./src
test-casper:
casperjs test ./test

View File

@@ -20,23 +20,17 @@ helpers = require('./helpers');
helpers.viewHelpers(view); helpers.viewHelpers(view);
get('/:uidParam?', function(page, model, _arg, next) { get('/:uidParam?', function(page, model, _arg, next) {
var q, sess, uidParam, userId; var sess, uidParam;
uidParam = _arg.uidParam; uidParam = _arg.uidParam;
if (uidParam === 'privacy' || uidParam === 'terms') { if (uidParam === 'privacy' || uidParam === 'terms' || uidParam === 'auth') {
return next(); return next();
} }
sess = model.session; sess = model.session;
if (sess.auth && sess.auth.facebook) { if (sess.habitRpgAuth && sess.habitRpgAuth.facebook) {
q = model.query('users').withEveryauth('facebook', sess.auth.facebook.id); model.set('_facebookAuthenticated', true);
model.fetch(q, function(err, user) {
if (user && user.get('id') !== sess.auth.id) {
sess.auth.id = user.get('id');
return page.redirect('/');
}
});
} }
userId = model.get('_userId'); model.set('_userId', sess.userId);
return model.subscribe("users." + userId, function(err, user) { return model.subscribe("users." + sess.userId, function(err, user) {
model.ref('_user', user); model.ref('_user', user);
model.set('_items', { model.set('_items', {
armor: content.items.armor[parseInt(user.get('items.armor')) + 1], armor: content.items.armor[parseInt(user.get('items.armor')) + 1],

View File

@@ -1,80 +1,81 @@
// Generated by CoffeeScript 1.3.3 // Generated by CoffeeScript 1.3.3
var conf, derby, model, sess; var conf, content, derby, req, schema;
conf = require("./conf"); conf = require("./conf");
derby = require('derby'); derby = require('derby');
model = void 0; schema = require('../app/schema');
sess = void 0; content = require('../app/content');
module.exports.setupPurlAuth = function(req) { req = void 0;
var acceptableUid, uidParam;
module.exports.setRequest = function(r) {
return req = r;
};
module.exports.newUserAndPurl = function() {
var acceptableUid, guid, model, newUser, sess, task, uidParam, _i, _len, _ref;
model = req.getModel(); model = req.getModel();
sess = req.session; sess = model.session;
sess.userId || (sess.userId = derby.uuid());
sess.auth || (sess.auth = {
userId: sess.userId
});
uidParam = req.url.split('/')[1]; uidParam = req.url.split('/')[1];
if (!sess.userId) {
sess.userId = derby.uuid();
newUser = require('node.extend')(true, {}, schema.userSchema);
_ref = content.defaultTasks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
task = _ref[_i];
guid = task.id = require('derby/node_modules/racer').uuid();
newUser.tasks[guid] = task;
switch (task.type) {
case 'habit':
newUser.habitIds.push(guid);
break;
case 'daily':
newUser.dailyIds.push(guid);
break;
case 'todo':
newUser.todoIds.push(guid);
break;
case 'reward':
newUser.rewardIds.push(guid);
}
}
model.set("users." + sess.userId, newUser);
}
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9'); acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9');
if (acceptableUid && sess.userId !== uidParam) { if (acceptableUid && sess.userId !== uidParam) {
sess.userId = uidParam; return sess.userId = uidParam;
} }
return model.set('_userId', sess.userId);
}; };
module.exports.setupEveryauth = function(everyauth) { module.exports.setupEveryauth = function(everyauth) {
everyauth.debug = true; everyauth.debug = true;
everyauth.everymodule.findUserById(function(id, callback) { everyauth.everymodule.findUserById(function(id, callback) {
return model.fetch("users." + id, function(err, user) { return callback(null, null);
var content, guid, newUser, schema, task, _i, _len, _ref;
if (user && user.get('id')) {
return callback(null, user.get());
} else {
schema = require('../app/schema');
content = require('../app/content');
newUser = require('node.extend')(true, {}, schema.userSchema);
_ref = content.defaultTasks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
task = _ref[_i];
guid = task.id = require('derby/node_modules/racer').uuid();
newUser.tasks[guid] = task;
switch (task.type) {
case 'habit':
newUser.habitIds.push(guid);
break;
case 'daily':
newUser.dailyIds.push(guid);
break;
case 'todo':
newUser.todoIds.push(guid);
break;
case 'reward':
newUser.rewardIds.push(guid);
}
}
model.set("users." + id, newUser);
return callback(null, newUser);
}
});
}); });
return everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) { return everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) {
var q; var model, q;
session.habitRpgAuth || (session.habitRpgAuth = {});
session.habitRpgAuth.facebook = fbUserMetadata.id;
model = req.getModel();
q = model.query('users').withEveryauth('facebook', fbUserMetadata.id); q = model.query('users').withEveryauth('facebook', fbUserMetadata.id);
model.fetch(q, function(err, user) { model.fetch(q, function(err, user) {
var id;
id = user && user.get() && user.get()[0].id;
console.log({ console.log({
err: err, err: err,
user: user id: id,
fbUserMetadata: fbUserMetadata
}); });
if (user.get('id')) { if (id && id !== session.userId) {
return sess.userId = user.get('id'); return session.userId = id;
} else { } else {
model.setNull("users." + sess.userId + ".auth", { model.setNull("users." + session.userId + ".auth", {
'facebook': {} 'facebook': {}
}); });
return model.set("users." + sess.userId + ".auth.facebook", fbUserMetadata); return model.set("users." + session.userId + ".auth.facebook", fbUserMetadata);
} }
}); });
return fbUserMetadata; return fbUserMetadata;

View File

@@ -50,6 +50,8 @@ auth.setupQueries(store);
auth.setupEveryauth(everyauth); auth.setupEveryauth(everyauth);
auth.setupAccessControl(store);
ONE_YEAR = 1000 * 60 * 60 * 24 * 365; ONE_YEAR = 1000 * 60 * 60 * 24 * 365;
root = path.dirname(path.dirname(__dirname)); root = path.dirname(path.dirname(__dirname));
@@ -59,11 +61,9 @@ publicPath = path.join(root, 'public');
habitrpgMiddleware = function(req, res, next) { habitrpgMiddleware = function(req, res, next) {
var model; var model;
model = req.getModel(); model = req.getModel();
auth.setupPurlAuth(req);
auth.setupAccessControl(store);
model.set('_stripePubKey', process.env.STRIPE_PUB_KEY);
model.set('_nodeEnv', process.env.NODE_ENV);
model.set('_mobileDevice', /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent'))); model.set('_mobileDevice', /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')));
auth.setRequest(req);
auth.newUserAndPurl();
return next(); return next();
}; };
@@ -79,4 +79,4 @@ expressApp.use(express.favicon()).use(gzippo.staticGzip(publicPath, {
}) })
})).use(store.modelMiddleware()).use(habitrpgMiddleware).use(everyauth.middleware()).use(app.router()).use(expressApp.router).use(serverError(root)); })).use(store.modelMiddleware()).use(habitrpgMiddleware).use(everyauth.middleware()).use(app.router()).use(expressApp.router).use(serverError(root));
require('./serverRoutes')(expressApp); require('./serverRoutes')(expressApp, root, derby);

View File

@@ -3,7 +3,7 @@ var scoring;
scoring = require('../app/scoring'); scoring = require('../app/scoring');
module.exports = function(expressApp) { module.exports = function(expressApp, root, derby) {
expressApp.get('/:uid/up/:score?', function(req, res) { expressApp.get('/:uid/up/:score?', function(req, res) {
var model, score; var model, score;
score = parseInt(req.params.score) || 1; score = parseInt(req.params.score) || 1;
@@ -50,10 +50,10 @@ module.exports = function(expressApp) {
staticPages = derby.createStatic(root); staticPages = derby.createStatic(root);
return staticPages.render('terms', res); return staticPages.render('terms', res);
}); });
expressApp.all('*', function(req) { expressApp.post('/', function(req) {
throw "404: " + req.url;
});
return expressApp.post('/', function(req) {
return require('../app/reroll').stripeResponse(req); return require('../app/reroll').stripeResponse(req);
}); });
return expressApp.all('*', function(req) {
throw "404: " + req.url;
});
}; };

View File

@@ -16,20 +16,13 @@ helpers.viewHelpers(view)
get '/:uidParam?', (page, model, {uidParam}, next) -> get '/:uidParam?', (page, model, {uidParam}, next) ->
#FIXME figure out a better way to do this #FIXME figure out a better way to do this
return next() if (uidParam == 'privacy' or uidParam == 'terms') return next() if (uidParam == 'privacy' or uidParam == 'terms' or uidParam == 'auth')
sess = model.session sess = model.session
if sess.auth && sess.auth.facebook if sess.habitRpgAuth && sess.habitRpgAuth.facebook
q = model.query('users').withEveryauth('facebook', sess.auth.facebook.id) model.set('_facebookAuthenticated', true)
model.fetch q, (err, user) -> model.set '_userId', sess.userId
if (user && user.get('id')!=sess.auth.id) model.subscribe "users.#{sess.userId}", (err, user) ->
sess.auth.id = user.get('id')
return page.redirect('/')
userId = model.get '_userId'
model.subscribe "users.#{userId}", (err, user) ->
model.ref '_user', user model.ref '_user', user
# Store # Store

View File

@@ -1,58 +1,50 @@
conf = require("./conf") conf = require("./conf")
derby = require('derby') derby = require('derby')
schema = require('../app/schema')
content = require('../app/content')
# FIXME I need access to model in everyauth, so that I can test whether a user exists in the database or # Need this for later use by EveryAuth in the MiddleWare
# create a new one otherwise. Everyauth configs must be declared before expressApp.use(...); howeve,r model is req = undefined
# setup during expresApp.use(...). So my hack is - declare model here, define everyauth configs early in the server (like module.exports.setRequest = (r) ->
# we should), then use our own middleware (habitrpgMiddleware) to model during expresApp.use(...) req = r
model = undefined
sess = undefined
# Ultimate goal: set `sess.auth.userId` module.exports.newUserAndPurl = () ->
## PURL authentication
# This is temporary and will go away when everyauth is setup. It tests to see if
# a UUID was used (bookmarked private url), and restores the user session if so
module.exports.setupPurlAuth = (req) ->
model = req.getModel() model = req.getModel()
sess = req.session sess = model.session
# Setup userId for new users
sess.userId ||= derby.uuid()
sess.auth ||= {userId: sess.userId} # prepare for everyauth
# Previously saved session (eg, http://localhost/{guid}) (temporary solution until authentication built)
uidParam = req.url.split('/')[1] uidParam = req.url.split('/')[1]
## -------- (1) New user --------
# They get to play around before creating a new account.
unless sess.userId
sess.userId = derby.uuid()
# deep clone, else further new users get duplicate objects
newUser = require('node.extend')(true, {}, schema.userSchema)
for task in content.defaultTasks
guid = task.id = require('derby/node_modules/racer').uuid()
newUser.tasks[guid] = task
switch task.type
when 'habit' then newUser.habitIds.push guid
when 'daily' then newUser.dailyIds.push guid
when 'todo' then newUser.todoIds.push guid
when 'reward' then newUser.rewardIds.push guid
model.set "users.#{sess.userId}", newUser
## -------- (2) PURL --------
# eg, http://localhost/{guid}), legacy - will be removed eventually
# tests if UUID was used (bookmarked private url), and restores that session
acceptableUid = require('guid').isGuid(uidParam) or (uidParam in ['3','9']) acceptableUid = require('guid').isGuid(uidParam) or (uidParam in ['3','9'])
if acceptableUid && sess.userId!=uidParam if acceptableUid && sess.userId!=uidParam
# TODO test whether user exists: ```model.fetch("users.#{uidParam}", function(err,user){if(user.get(..){})}})```, but doesn't seem to work # TODO check if in database - issue with accessControl which is on current uid?
sess.userId = uidParam sess.userId = uidParam
model.set '_userId', sess.userId
## Setup callbacks for serializing/deserializing users to/from model.
module.exports.setupEveryauth = (everyauth) -> module.exports.setupEveryauth = (everyauth) ->
everyauth.debug = true everyauth.debug = true
everyauth.everymodule.findUserById (id, callback) -> everyauth.everymodule.findUserById (id, callback) ->
model.fetch "users.#{id}", (err, user) -> # will never be called, can't fetch user from database at this point on the server
if user && user.get('id') # see https://github.com/codeparty/racer/issues/39. Handled in app/auth.coffee for now
callback null, user.get() # FIXME what format are we supposed to return user? callback null, null
else
# Create new user if none exists
# deep clone, else further new users get duplicate objects
schema = require('../app/schema')
content = require('../app/content')
newUser = require('node.extend')(true, {}, schema.userSchema)
for task in content.defaultTasks
guid = task.id = require('derby/node_modules/racer').uuid()
newUser.tasks[guid] = task
switch task.type
when 'habit' then newUser.habitIds.push guid
when 'daily' then newUser.dailyIds.push guid
when 'todo' then newUser.todoIds.push guid
when 'reward' then newUser.rewardIds.push guid
model.set "users.#{id}", newUser
callback null, newUser
# Facebook Authentication Logic # Facebook Authentication Logic
everyauth everyauth
@@ -60,29 +52,28 @@ module.exports.setupEveryauth = (everyauth) ->
.appId(process.env.FACEBOOK_KEY) .appId(process.env.FACEBOOK_KEY)
.appSecret(process.env.FACEBOOK_SECRET) .appSecret(process.env.FACEBOOK_SECRET)
.findOrCreateUser( (session, accessToken, accessTokenExtra, fbUserMetadata) -> .findOrCreateUser( (session, accessToken, accessTokenExtra, fbUserMetadata) ->
# usersByFbId[fbUserMetadata.id] or (usersByFbId[fbUserMetadata.id] = addUser("facebook", fbUserMetadata))
# Put it in the session for later use
# FIXME shouldn't this be set by everyauth? (session.auth.facebook)
session.habitRpgAuth ||= {}
session.habitRpgAuth.facebook = fbUserMetadata.id
model = req.getModel()
q = model.query('users').withEveryauth('facebook', fbUserMetadata.id) q = model.query('users').withEveryauth('facebook', fbUserMetadata.id)
model.fetch q, (err,user) -> model.fetch q, (err, user) ->
console.log {err:err,user:user} #FIXME this is always returning user:null, however; this runs fine on the client id = user && user.get() && user.get()[0].id
if user.get('id') console.log {err:err, id:id, fbUserMetadata:fbUserMetadata}
sess.userId = user.get('id') # is necessary? # Has user been tied to facebook account already?
if (id && id!=session.userId)
session.userId = id
# Else tie user to their facebook account
else else
model.setNull "users.#{sess.userId}.auth", {'facebook':{}} model.setNull "users.#{session.userId}.auth", {'facebook':{}}
model.set "users.#{sess.userId}.auth.facebook", fbUserMetadata model.set "users.#{session.userId}.auth.facebook", fbUserMetadata
fbUserMetadata fbUserMetadata
).redirectPath "/" ).redirectPath "/"
# addUser = (source, sourceUser) ->
# user = undefined
# if arguments.length is 1 # password-based
# user = sourceUser = source
# user.id = ++nextUserId
# return usersById[nextUserId] = user
# else # non-password-based
# user = usersById[++nextUserId] = id: nextUserId
# user[source] = sourceUser
# user
module.exports.setupQueries = (store) -> module.exports.setupQueries = (store) ->
## Setup Queries ## Setup Queries
store.query.expose 'users', 'withId', (id) -> store.query.expose 'users', 'withId', (id) ->

View File

@@ -29,6 +29,7 @@ store = derby.createStore
listen: server listen: server
auth.setupQueries(store) auth.setupQueries(store)
auth.setupEveryauth(everyauth) auth.setupEveryauth(everyauth)
auth.setupAccessControl(store)
ONE_YEAR = 1000 * 60 * 60 * 24 * 365 ONE_YEAR = 1000 * 60 * 60 * 24 * 365
root = path.dirname path.dirname __dirname root = path.dirname path.dirname __dirname
@@ -36,16 +37,10 @@ publicPath = path.join root, 'public'
habitrpgMiddleware = (req, res, next) -> habitrpgMiddleware = (req, res, next) ->
model = req.getModel() model = req.getModel()
auth.setupPurlAuth(req)
auth.setupAccessControl(store)
model.set '_stripePubKey', process.env.STRIPE_PUB_KEY
model.set '_nodeEnv', process.env.NODE_ENV
## Set _mobileDevice to true or false so view can exclude portions from mobile device ## Set _mobileDevice to true or false so view can exclude portions from mobile device
model.set '_mobileDevice', /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header 'User-Agent') model.set '_mobileDevice', /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header 'User-Agent')
auth.setRequest(req) # Need to pass into auth, so auth can save as private variable used later by EveryAuth
auth.newUserAndPurl()
next() next()
expressApp expressApp
@@ -79,4 +74,4 @@ expressApp
.use(expressApp.router) .use(expressApp.router)
.use(serverError root) .use(serverError root)
require('./serverRoutes')(expressApp) require('./serverRoutes')(expressApp, root, derby)

View File

@@ -1,6 +1,6 @@
scoring = require('../app/scoring') scoring = require('../app/scoring')
module.exports = (expressApp) -> module.exports = (expressApp, root, derby) ->
expressApp.get '/:uid/up/:score?', (req, res) -> expressApp.get '/:uid/up/:score?', (req, res) ->
score = parseInt(req.params.score) || 1 score = parseInt(req.params.score) || 1
@@ -28,8 +28,8 @@ module.exports = (expressApp) ->
staticPages = derby.createStatic root staticPages = derby.createStatic root
staticPages.render 'terms', res staticPages.render 'terms', res
expressApp.all '*', (req) ->
throw "404: #{req.url}"
expressApp.post '/', (req) -> expressApp.post '/', (req) ->
require('../app/reroll').stripeResponse(req) require('../app/reroll').stripeResponse(req)
expressApp.all '*', (req) ->
throw "404: #{req.url}"

View File

@@ -11,16 +11,14 @@
<ui:connectionAlert> <ui:connectionAlert>
<div id="head"> <div id="head">
<div class='pull-right'> <div class='pull-right'>
<a class='btn btn-small pull-right' onClick="$('#copy-link-section').toggle();"><i class=icon-bookmark></i></a><br/> {#unless _facebookAuthenticated}
<div id=copy-link-section style='display:none;' class=well> <a href="/auth/facebook" class='btn btn-small pull-right'><i class=icon-user></i>Login / Signup With Facebook</a>
<h3>Secret Link</h3> {else}
<p>Authentication isn't yet available (<a href="https://github.com/lefnire/habitrpg#how-do-i-log-in--save-my-data">follow progress here</a>),<br/> <span class=label>{_user.auth.facebook.name}</span>
In the meantime a persistent URL can be used accross <br/> {/}
browsers to access your data. Bookmark the following URL.</p>
<input id='purl' type=text class=input-xlarge value="" />
</div>
</div> </div>
<div class='container-fluid'> <div class='container-fluid'>
<div class='row-fluid'> <div class='row-fluid'>
<div id=character class='span4'> <div id=character class='span4'>