Skip to content
This repository was archived by the owner on Oct 23, 2019. It is now read-only.

Commit 7add8e2

Browse files
author
Justin Slattery
committed
Initial purgatory implementation complete. Adding all depenencies
1 parent 49523bb commit 7add8e2

File tree

2,436 files changed

+499236
-254
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,436 files changed

+499236
-254
lines changed

config.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"predef": [
3+
"jasmine",
4+
"spyOn",
5+
"it",
6+
"describe",
7+
"expect"
8+
],
9+
10+
"node" : true,
11+
"es5" : true,
12+
"browser" : true,
13+
"boss" : false,
14+
"curly": false,
15+
"debug": false,
16+
"devel": false,
17+
"eqeqeq": true,
18+
"evil": false,
19+
"forin": false,
20+
"immed": true,
21+
"laxbreak": true,
22+
"newcap": true,
23+
"noarg": true,
24+
"noempty": false,
25+
"nonew": false,
26+
"nomen": false,
27+
"onevar": true,
28+
"plusplus": false,
29+
"regexp": false,
30+
"undef": true,
31+
"sub": true,
32+
"strict": false,
33+
"white": true
34+
}
35+

controllers/controllers.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,27 @@
33
//
44
var NodeChatController = {
55
init: function(options) {
6-
this.socket = new io.Socket(null, {port: 8000});
7-
var mysocket = this.socket;
8-
this.userName = options.userName;
9-
this.hashPass = options.hashPass;
6+
var mySocket, user, view, hashpassword;
107

11-
this.model = new models.NodeChatModel({userName: this.userName, hashPass: this.hashPass});
8+
this.socket = new io.Socket(null, {port: 8001});
9+
mySocket = this.socket;
10+
user = this.userName = options.userName;
11+
hashpassword = this.hashpassword = options.hashpassword
12+
13+
this.model = new models.NodeChatModel();
1214
this.view = new NodeChatView({model: this.model, socket: this.socket, el: $('#content')});
13-
var view = this.view;
15+
view = this.view;
16+
17+
this.socket.on('connect', function () {
18+
19+
mySocket.send({
20+
event: 'clientauthrequest'
21+
, user: user
22+
, hashpassword: hashpassword
23+
});
24+
25+
log('Connected! Oh hai!');
26+
});
1427

1528
this.socket.on('message', function(msg) {view.msgReceived(msg)});
1629
this.socket.connect();

core.js

Lines changed: 91 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -33,87 +33,91 @@ var express = require('express')
3333
, app = express.createServer()
3434
, connect = require('connect')
3535
, jade = require('jade')
36-
, socket = require('socket.io').listen(app)
37-
, RedisStore = require('connect-redis');
36+
, socket = require('socket.io').listen(app);
3837

3938
app.set('view engine', 'jade');
4039
app.set('view options', {layout: false});
4140
app.use(express.bodyParser());
42-
app.use(express.cookieParser());
43-
app.use(express.session({ store: new RedisStore(), secret: 'Secretly I am an elephant' }));
4441

45-
/*
46-
* Route: GET /login
47-
*
48-
* Template: login.jade
49-
*/
50-
app.get('/login', function (req, res) {
51-
res.render('login');
52-
});
53-
54-
/*
55-
* Route: POST /login
56-
*
57-
* Calls the authentication module to verify login details. Failures are redirected back to the login page.
58-
*
59-
* If the authentication module gives us a user object back, we ask connect to regenerate the session and send the client back to index. Note: we specify a _long_ cookie age so users won't have to log in frequently. We also set the httpOnly flag to false (I know, not so secure) to make the cookie available over [Flash Sockets](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/Socket.html).
60-
*/
61-
app.post('/login', function (req, res) {
62-
auth.authenticateUser(req.body.username, req.body.password, function (err, user) {
63-
if (user) {
64-
// Regenerate session when signing in to prevent fixation
65-
req.session.regenerate(function () {
66-
// Store the user's primary key in the session store to be retrieved, or in this case the entire user object
67-
console.log('regenerated session id ' + req.session.id);
68-
req.session.cookie.maxAge = 100 * 24 * 60 * 60 * 1000; //Force longer cookie age
69-
req.session.cookie.httpOnly = false;
70-
req.session.user = user;
42+
function signInAccount(req, res) {
43+
if (req.body.email) {
44+
auth.createNewUserAccount(req.body.username, req.body.password, req.body.email, function (err, user) {
45+
if ((err) || (!user)) {
46+
es.redirect('back');
47+
}
48+
else if (user) {
49+
res.render('index', {
50+
locals: { name: user.name, hashpassword: JSON.stringify(user.hashpassword) }
51+
});
52+
}
53+
});
54+
}
55+
else {
56+
auth.authenticateUser(req.body.username, req.body.password, function (err, user) {
57+
if (err) {
58+
winston.error('[signInAccount][authenticateUser][fn] Error: ' + err);
59+
}
60+
61+
if (user) {
62+
res.render('index', {
63+
locals: { name: user.name, hashpassword: JSON.stringify(user.hashPass) }
64+
});
65+
}
66+
else {
67+
res.redirect('back');
68+
}
69+
});
70+
}
71+
}
7172

72-
console.log('Storing new hash for user ' + user.get('name') + ': ' + req.session.user.get('hashPass'));
73-
res.redirect('/');
73+
function purgatory() {
74+
var inPurgatory = true;
75+
return {
76+
tryToGetOut: function (message, client, cb) {
77+
if (!message || !message.user || !message.hashpassword) {
78+
winston.info('[purgatory][tryToGetOut] Client with no user/hash attempting message. Client still in purgatory');
79+
return;
80+
}
81+
auth.authenticateUserByHash(message.user, message.hashpassword, function(err, data) {
82+
if (err) {
83+
winston.info('[purgatory] Bad auth. Client still in purgatory');
84+
inPurgatory = true;
85+
}
86+
else {
87+
winston.info('[purgatory] out of purgatory');
88+
inPurgatory = false;
89+
90+
//Once we are sure the client is who s/he claims to be, attach name and hash for future use.
91+
client.user = message.user;
92+
client.hashpassword = message.hashpassword;
93+
94+
cb && cb();
95+
}
7496
});
75-
} else {
76-
req.session.error = 'Authentication failed, please check your username and password.';
77-
res.redirect('back');
7897
}
79-
});
80-
});
98+
, stillInPurgatory: function() {
99+
winston.info('[purgatory] status ' + inPurgatory);
100+
return inPurgatory;
101+
}
102+
}
103+
}
81104

82105
/*
83-
* Route: GET /signup:
106+
* Route: POST /
84107
*
85-
* Template: signup.jade
86-
*/
87-
app.get('/signup', function (req, res) {
88-
res.render('signup');
89-
});
90-
91-
/*
92-
* Route: POST /signup
108+
* Calls the authentication module to verify login details. Failures are redirected back to the login page.
93109
*
94-
* Calls createNewUserAccount() in the auth module, redirects to /login if a user object is returned. Redirects to /signup if not.
95-
*/
96-
app.post('/signup', function (req, res) {
97-
auth.createNewUserAccount(req.body.username, req.body.password1, req.body.password2, req.body.email, req.body.ponies, function (err, user) {
98-
if ((err) || (!user)) {
99-
req.session.error = 'New user failed, please check your username and password.';
100-
res.redirect('back');
101-
}
102-
else if (user) {
103-
res.redirect('/login');
104-
}
105-
});
106-
110+
* If the authentication module gives us a user object back, we ask connect to regenerate the session and send the client back to index. Note: we specify a _long_ cookie age so users won't have to log in frequently. We also set the httpOnly flag to false (I know, not so secure) to make the cookie available over [Flash Sockets](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/Socket.html).
111+
*/
112+
app.post('/', function (req, res) {
113+
signInAccount(req, res)
107114
});
108115

109116
/*
110-
* Tell connect to destory the session.
117+
* redirect back to the login page
111118
*/
112-
113119
app.get('/logout', function (req, res) {
114-
req.session.destroy(function () {
115-
res.redirect('home');
116-
});
120+
res.redirect('/');
117121
});
118122

119123

@@ -127,47 +131,18 @@ app.get('/*.(js|css)', function (req, res) {
127131
});
128132

129133
/*
130-
* Middleware that decides what a valid login looks like. In this case, just verify that we have a session object for the user.
131-
*
132-
* This is an express [route middleware](http://expressjs.com/guide.html#route-middleware). Control is passed to the middleware function before the route function is called. We use restrictAccess() to verify that we have a valid user key in the session, implying that authentication has succeeded, before we send the client to the index.jade template. If we do not have a valid user in the session, then we redirect to the '/login' route. This effectively locks down our '/' route from unauthenticated access. You could add the restrictAccess() all to any route you want to protect.
133-
*/
134-
function restrictAccess(req, res, next) {
135-
if (req.session.user) {
136-
next();
137-
} else {
138-
req.session.error = 'Access denied!';
139-
res.redirect('/login');
140-
}
141-
}
142-
143-
/*
144-
* Server up the main index view.
145-
*
146-
* Calls the restrictAccess() middleware.
134+
* Server up login page.
147135
*
136+
* Template: login.jade
148137
*/
149-
app.get('/', restrictAccess, function (req, res) {
150-
res.render('index', {
151-
locals: { name: req.session.user.name, hashPass: JSON.stringify(req.session.user.hashPass) }
152-
});
138+
app.get('/', function (req, res) {
139+
res.render('login');
153140
});
154141

155142
//create local state
156143
var activeClients = 0;
157144
var nodeChatModel = new models.NodeChatModel();
158145

159-
/*
160-
* When we have a client that shouldn't be connected, __kick 'em off!__'
161-
*
162-
* @param {object} client
163-
* @param {function} fn
164-
*/
165-
function disconnectAndRedirectClient(client, fn) {
166-
console.log('Disconnecting unauthenticated user');
167-
client.send({ event: 'disconnect' });
168-
client.connection.end();
169-
return fn();
170-
}
171146

172147
/*
173148
* Event handler for client disconnects. Simply broadcasts the new active client count.
@@ -189,13 +164,13 @@ function clientDisconnect(client) {
189164
function chatMessage(client, socket, msg) {
190165
var chat = new models.ChatEntry();
191166
chat.mport(msg);
167+
chat.set({name: client.user});
192168

193169
rc.incr('next.chatentry.id', function (err, newId) {
194170
chat.set({id: newId});
195171
nodeChatModel.chats.add(chat);
196172

197-
var expandedMsg = chat.get('id') + ' ' + client.user.name + ': ' + chat.get('text');
198-
console.log('(' + client.sessionId + ') ' + expandedMsg);
173+
var expandedMsg = chat.get('id') + ' ' + client.user + ': ' + chat.get('text');
199174

200175
rc.rpush('chatentries', chat.xport(), redis.print);
201176

@@ -209,70 +184,30 @@ function chatMessage(client, socket, msg) {
209184
/*
210185
* Handle the new connection event for socket.
211186
*
212-
* connectSession() is a helper method that will verify a client's validity by checking for a cookie in the request header, then, if we find it, _pulling their session out of redis_.
213-
*
214-
* We then use the helper method in the 'connection' handler for our socket listener. Instead accepting any user connection, we are going to check that the client has a valid session (meaning they logged in). If they don't, give them the boot! If they do, then we store a copy of the session data (yay we have access!) in the client object and then setup the rest of the socket events. Finally, send them a welcome message just to prove that we remembered their profile.
215187
*/
216188
socket.on('connection', function (client) {
217-
client.connectSession = function (fn) {
218-
if (!client.request || !client.request.headers || !client.request.headers.cookie) {
219-
disconnectAndRedirectClient(client, function () {
220-
console.log('Null request/header/cookie!');
221-
});
222-
return;
189+
var clientPurgatory = purgatory();
190+
191+
client.on('message', function(message) {
192+
if (clientPurgatory.stillInPurgatory()) {
193+
if(message.event === 'clientauthrequest') {
194+
//If we can get out of purgatory, set up the client for pubsub
195+
clientPurgatory.tryToGetOut(message, client, function () {
196+
197+
client.on('disconnect', function () {
198+
clientDisconnect(client);
199+
});
200+
});
201+
}
223202
}
224-
225-
console.log('Cookie is' + client.request.headers.cookie);
226-
227-
var match, sid;
228-
match = client.request.headers.cookie.match(/connect\.sid=([^;]+)/);
229-
if (!match || match.length < 2) {
230-
disconnectAndRedirectClient(client, function () {
231-
console.log('Failed to find connect.sid in cookie');
232-
});
233-
return;
203+
else {
204+
//If this is called, we are not in purgatory, so handle it normally
205+
chatMessage(client, socket, message);
234206
}
235-
236-
sid = unescape(match[1]);
237-
238-
rc.get(sid, function (err, data) {
239-
fn(err, JSON.parse(data));
240-
});
241-
};
242-
243-
client.connectSession(function (err, data) {
244-
if (err) {
245-
console.log('Error on connectionSession: ' + err);
246-
return;
247-
}
248-
249-
client.user = data.user;
250-
251-
activeClients += 1;
252-
client.on('disconnect', function () {
253-
clientDisconnect(client);
254-
});
255-
client.on('message', function (msg) {
256-
chatMessage(client, socket, msg);
257-
});
258-
259-
console.log('User successfully connected with ' + data.user.name + ' hash ' + data.user.hashPass);
260-
261-
socket.broadcast({
262-
event: 'update',
263-
clients: activeClients
264-
});
265-
266-
var ponyWelcome = new models.ChatEntry({name: 'PonyBot', text: 'Hello ' + data.user.name + '. I also feel that ponies ' + data.user.ponies + '. Welcome to nodechat.js'});
267-
268-
socket.broadcast({
269-
event: 'chat',
270-
data: ponyWelcome.xport()
271-
});
272207
});
273208
});
274209

275210
/*
276211
* Fire up the webserver
277212
*/
278-
app.listen(8000);
213+
app.listen(8001);

0 commit comments

Comments
 (0)