Merge pull request #306 from 3rd-Eden/namespace.auth

Namespace.auth
This commit is contained in:
Guillermo Rauch
2011-06-29 04:14:29 -07:00
4 changed files with 271 additions and 73 deletions

View File

@@ -24,6 +24,7 @@ function SocketNamespace (mgr, name) {
this.manager = mgr;
this.name = name || '';
this.sockets = {};
this.auth = false;
this.setFlags();
};
@@ -199,6 +200,17 @@ SocketNamespace.prototype.socket = function (sid, readable) {
return this.sockets[sid];
};
/**
* Sets authorization for this namespace
*
* @api public
*/
SocketNamespace.prototype.authorization = function (fn) {
this.auth = fn;
return this;
};
/**
* Called when a socket disconnects entirely.
*
@@ -211,6 +223,30 @@ SocketNamespace.prototype.handleDisconnect = function (sid, reason) {
}
};
/**
* Performs authentication.
*
* @param Object client request data
* @api private
*/
SocketNamespace.prototype.authorize = function (data, fn) {
if (this.auth) {
var self = this;
this.auth.call(this, data, function (err, authorized) {
self.log.debug('client ' +
(authorized ? '' : 'un') + 'authorized for ' + self.name);
fn(err, authorized);
});
} else {
this.log.debug('client authorized for ' + this.name);
fn(null, true);
}
return this;
};
/**
* Handles a packet.
*
@@ -231,16 +267,42 @@ SocketNamespace.prototype.handlePacket = function (sessid, packet) {
});
};
function error (err) {
self.log.warn('handshake error ' + err + ' for ' + self.name);
socket.packet({ type: 'error', reason: err });
};
function connect () {
self.manager.onJoin(sessid, self.name);
self.store.publish('join', sessid, self.name);
// packet echo
socket.packet({ type: 'connect' });
// emit connection event
self.emit('connection', socket);
};
switch (packet.type) {
case 'connect':
this.manager.onJoin(sessid, this.name);
this.store.publish('join', sessid, this.name);
if (packet.endpoint == '') {
connect();
} else {
var manager = this.manager
, handshakeData = manager.handshaken[sessid];
// packet echo
socket.packet({ type: 'connect' });
this.authorize(handshakeData, function (err, authorized, newData) {
if (err) return error(err);
// emit connection event
self.emit('connection', socket);
if (authorized) {
manager.onHandshake(sessid, newData || handshakeData);
self.store.publish('handshake', sessid, newData || handshakeData);
connect();
} else {
error('unauthorized');
}
});
}
break;
case 'ack':

View File

@@ -12,7 +12,8 @@
var io = require('socket.io')
, parser = io.parser
, http = require('http')
, https = require('https');
, https = require('https')
, WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket;
/**
* Exports.
@@ -181,3 +182,61 @@ create = function (cl) {
console.log('');
return io.listen(cl.port);
};
/**
* WebSocket socket.io client.
*
* @api private
*/
function WSClient (port, sid) {
this.sid = sid;
this.port = port;
WebSocket.call(
this
, 'ws://localhost:' + port + '/socket.io/'
+ io.protocol + '/websocket/' + sid
);
};
/**
* Inherits from WebSocket.
*/
WSClient.prototype.__proto__ = WebSocket.prototype;
/**
* Overrides message event emission.
*
* @api private
*/
WSClient.prototype.emit = function (name) {
var args = arguments;
if (name == 'message' || name == 'data') {
args[1] = parser.decodePacket(args[1].toString());
}
return WebSocket.prototype.emit.apply(this, arguments);
};
/**
* Writes a packet
*/
WSClient.prototype.packet = function (pack) {
this.write(parser.encodePacket(pack));
return this;
};
/**
* Creates a websocket client.
*
* @api public
*/
websocket = function (cl, sid) {
return new WSClient(cl.port, sid);
};

143
test/namespace.test.js Normal file
View File

@@ -0,0 +1,143 @@
/*!
* socket.io-node
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
* MIT Licensed
*/
/**
* Test dependencies.
*/
var sio = require('socket.io')
, should = require('./common')
, ports = 15700;
/**
* Test.
*/
module.exports = {
'namespace pass no authentication': function (done) {
var cl = client(++ports)
, io = create(cl)
, ws;
io.of('/a')
.on('connection', function (socket) {
cl.end();
ws.finishClose();
io.server.close()
done();
});
cl.handshake(function (sid) {
ws = websocket(cl, sid);
ws.on('open', function () {
ws.packet({
type: 'connect'
, endpoint: '/a'
});
})
});
},
'namespace pass authentication': function (done) {
var cl = client(++ports)
, io = create(cl)
, ws;
io.of('/a')
.authorization(function (data, fn) {
fn(null, true);
})
.on('connection', function (socket) {
cl.end();
ws.finishClose();
io.server.close()
done();
});
cl.handshake(function (sid) {
ws = websocket(cl, sid);
ws.on('open', function () {
ws.packet({
type: 'connect'
, endpoint: '/a'
});
})
});
},
'namespace authentication handshake data': function (done) {
var cl = client(++ports)
, io = create(cl)
, ws;
io.of('/a')
.authorization(function (data, fn) {
data.foo = 'bar';
fn(null, true);
})
.on('connection', function (socket) {
socket.handshake.address.address.should.equal('127.0.0.1');
socket.handshake.address.port.should.equal(ports);
socket.handshake.headers.host.should.equal('localhost');
socket.handshake.headers.connection.should.equal('keep-alive');
socket.handshake.time.should.match(/GMT/);
socket.handshake.foo.should.equal('bar');
cl.end();
ws.finishClose();
io.server.close()
done();
});
cl.handshake(function (sid) {
ws = websocket(cl, sid);
ws.on('open', function () {
ws.packet({
type: 'connect'
, endpoint: '/a'
});
})
});
},
'namespace fail authentication': function (done) {
var cl = client(++ports)
, io = create(cl)
, calls = 0
, ws;
io.of('/a')
.authorization(function (data, fn) {
fn(null, false);
})
.on('connection', function (socket) {
throw new Error('Should not be called');
});
cl.handshake(function (sid) {
ws = websocket(cl, sid);
ws.on('open', function () {
ws.packet({
type: 'connect'
, endpoint: '/a'
});
});
ws.on('message', function (data) {
if (data.endpoint == '/a') {
data.type.should.eql('error');
data.reason.should.eql('unauthorized')
cl.end();
ws.finishClose();
io.server.close()
done();
}
})
});
}
};

View File

@@ -11,75 +11,9 @@
var sio = require('socket.io')
, should = require('./common')
, HTTPClient = should.HTTPClient
, WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket
, parser = sio.parser
, ports = 15400;
/**
* Exports WSClient.
*/
module.exports = exports = WSClient;
/**
* WebSocket socket.io client.
*
* @api private
*/
function WSClient (port, sid) {
this.sid = sid;
this.port = port;
WebSocket.call(
this
, 'ws://localhost:' + port + '/socket.io/'
+ sio.protocol + '/websocket/' + sid
);
};
/**
* Inherits from WebSocket.
*/
WSClient.prototype.__proto__ = WebSocket.prototype;
/**
* Overrides message event emission.
*
* @api private
*/
WSClient.prototype.emit = function (name) {
var args = arguments;
if (name == 'message' || name == 'data') {
args[1] = parser.decodePacket(args[1].toString());
}
return WebSocket.prototype.emit.apply(this, arguments);
};
/**
* Writes a packet
*/
WSClient.prototype.packet = function (pack) {
this.write(parser.encodePacket(pack));
return this;
};
/**
* Creates a websocket client.
*
* @api public
*/
function websocket (cl, sid) {
return new WSClient(cl.port, sid);
};
/**
* Tests.
*/