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('/');
} }
}); model.set('_userId', sess.userId);
} return model.subscribe("users." + sess.userId, function(err, user) {
userId = model.get('_userId');
return model.subscribe("users." + 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,40 +1,27 @@
// 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;
model = req.getModel(); module.exports.setRequest = function(r) {
sess = req.session; return req = r;
sess.userId || (sess.userId = derby.uuid());
sess.auth || (sess.auth = {
userId: sess.userId
});
uidParam = req.url.split('/')[1];
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9');
if (acceptableUid && sess.userId !== uidParam) {
sess.userId = uidParam;
}
return model.set('_userId', sess.userId);
}; };
module.exports.setupEveryauth = function(everyauth) { module.exports.newUserAndPurl = function() {
everyauth.debug = true; var acceptableUid, guid, model, newUser, sess, task, uidParam, _i, _len, _ref;
everyauth.everymodule.findUserById(function(id, callback) { model = req.getModel();
return model.fetch("users." + id, function(err, user) { sess = model.session;
var content, guid, newUser, schema, task, _i, _len, _ref; uidParam = req.url.split('/')[1];
if (user && user.get('id')) { if (!sess.userId) {
return callback(null, user.get()); sess.userId = derby.uuid();
} else {
schema = require('../app/schema');
content = require('../app/content');
newUser = require('node.extend')(true, {}, schema.userSchema); newUser = require('node.extend')(true, {}, schema.userSchema);
_ref = content.defaultTasks; _ref = content.defaultTasks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -55,26 +42,40 @@ module.exports.setupEveryauth = function(everyauth) {
newUser.rewardIds.push(guid); newUser.rewardIds.push(guid);
} }
} }
model.set("users." + id, newUser); model.set("users." + sess.userId, newUser);
return callback(null, newUser);
} }
}); acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9');
if (acceptableUid && sess.userId !== uidParam) {
return sess.userId = uidParam;
}
};
module.exports.setupEveryauth = function(everyauth) {
everyauth.debug = true;
everyauth.everymodule.findUserById(function(id, callback) {
return callback(null, null);
}); });
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,47 +1,23 @@
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]
acceptableUid = require('guid').isGuid(uidParam) or (uidParam in ['3','9'])
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
sess.userId = uidParam
model.set '_userId', sess.userId
## Setup callbacks for serializing/deserializing users to/from model. ## -------- (1) New user --------
module.exports.setupEveryauth = (everyauth) -> # They get to play around before creating a new account.
unless sess.userId
everyauth.debug = true sess.userId = derby.uuid()
everyauth.everymodule.findUserById (id, callback) ->
model.fetch "users.#{id}", (err, user) ->
if user && user.get('id')
callback null, user.get() # FIXME what format are we supposed to return user?
else
# Create new user if none exists
# deep clone, else further new users get duplicate objects # deep clone, else further new users get duplicate objects
schema = require('../app/schema')
content = require('../app/content')
newUser = require('node.extend')(true, {}, schema.userSchema) newUser = require('node.extend')(true, {}, schema.userSchema)
for task in content.defaultTasks for task in content.defaultTasks
guid = task.id = require('derby/node_modules/racer').uuid() guid = task.id = require('derby/node_modules/racer').uuid()
@@ -51,8 +27,24 @@ module.exports.setupEveryauth = (everyauth) ->
when 'daily' then newUser.dailyIds.push guid when 'daily' then newUser.dailyIds.push guid
when 'todo' then newUser.todoIds.push guid when 'todo' then newUser.todoIds.push guid
when 'reward' then newUser.rewardIds.push guid when 'reward' then newUser.rewardIds.push guid
model.set "users.#{id}", newUser model.set "users.#{sess.userId}", newUser
callback null, 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'])
if acceptableUid && sess.userId!=uidParam
# TODO check if in database - issue with accessControl which is on current uid?
sess.userId = uidParam
module.exports.setupEveryauth = (everyauth) ->
everyauth.debug = true
everyauth.everymodule.findUserById (id, callback) ->
# will never be called, can't fetch user from database at this point on the server
# see https://github.com/codeparty/racer/issues/39. Handled in app/auth.coffee for now
callback null, null
# 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'>