Permalink
Browse files

Add (optional) authentication step when connecting to shares

share.open() now accepts an object instead of a origin string, The object can contains an 'authentication' property which will be attached the user agent. Use this to store an identifier for the user e.g. OAuth token
  • Loading branch information...
1 parent 7b835a1 commit 8e49bcdd1760727def6083ef40720a14265b5f9f @daredevildave daredevildave committed Sep 21, 2012
@@ -20,7 +20,7 @@ else
Doc = require('./doc').Doc
class Connection
- constructor: (host) ->
+ constructor: (host, authentication) ->
# Map of docname -> doc
@docs = {}
@@ -37,7 +37,12 @@ class Connection
new SockJS(host)
else
new BCSocket(host, reconnect:true)
-
+
+ # Send authentication message
+ @socket.send({
+ "auth": if authentication then authentication else null
+ })
+
@socket.onmessage = (msg) =>
msg = JSON.parse(msg.data) if useSockJS?
if msg.auth is null
View
@@ -28,15 +28,15 @@ exports.open = do ->
# This is a private connection pool for implicitly created connections.
connections = {}
- getConnection = (origin) ->
+ getConnection = (origin, authentication) ->
if WEB?
location = window.location
# default to browserchannel
path = if useSockJS then 'sockjs' else 'channel'
origin ?= "#{location.protocol}//#{location.host}/#{path}"
unless connections[origin]
- c = new Connection origin
+ c = new Connection origin, authentication
del = -> delete connections[origin]
c.on 'disconnected', del
@@ -55,12 +55,16 @@ exports.open = do ->
if numDocs == 0
c.disconnect()
- (docName, type, origin, callback) ->
- if typeof origin == 'function'
- callback = origin
- origin = null
+ (docName, type, options, callback) ->
+ if typeof options == 'function'
+ callback = options
+ options = {}
- c = getConnection origin
+
+ origin = options.origin
+ authentication = options.authentication
+
+ c = getConnection origin, authentication
c.numDocs++
c.open docName, type, (error, doc) ->
if error
View
@@ -26,6 +26,9 @@ hat = require 'hat'
syncQueue = require './syncqueue'
+# Time (in ms) that the server will wait for an auth message from the client before closing the connection
+AUTH_TIMEOUT = 10000
+
# session should implement the following interface:
# headers
# address
@@ -53,6 +56,7 @@ exports.handler = (session, createAgent) ->
# Map from docName -> {queue, listener if open}
docState = {}
+
# We'll only handle one message from each client at a time.
handleMessage = (query) ->
@@ -301,26 +305,42 @@ exports.handler = (session, createAgent) ->
send msg
callback()
+ # Authentication process has failed, send error and stop session
+ failAuthentication = (error) ->
+ session.send {
+ auth: null,
+ error: error
+ }
+ session.stop()
+
+ # Wait for client to send an auth message, but don't wait forever
+ timeout = setTimeout () ->
+ failAuthentication('Timeout waiting for client auth message')
+ , AUTH_TIMEOUT
+
# We don't process any messages from the agent until they've authorized. Instead,
# they are stored in this buffer.
buffer = []
- session.on 'message', bufferMsg = (msg) -> buffer.push msg
-
- createAgent data, (error, agent_) ->
- if error
- # The client is not authorized, so they shouldn't try and reconnect.
- session.send {auth:null, error}
- session.stop()
+ session.on 'message', bufferMsg = (msg) ->
+ if typeof msg.auth != 'undefined'
+ clearTimeout timeout
+ data.authentication = msg.auth
+ createAgent data, (error, agent_) ->
+ if error
+ # The client is not authorized, so they shouldn't try and reconnect.
+ failAuthentication(error)
+ else
+ agent = agent_
+ session.send auth:agent.sessionId
+
+ # Ok. Now we can handle all the messages in the buffer. They'll go straight to
+ # handleMessage from now on.
+ session.removeListener 'message', bufferMsg
+ handleMessage msg for msg in buffer
+ buffer = null
+ session.on 'message', handleMessage
else
- agent = agent_
- session.send auth:agent.sessionId
-
- # Ok. Now we can handle all the messages in the buffer. They'll go straight to
- # handleMessage from now on.
- session.removeListener 'message', bufferMsg
- handleMessage msg for msg in buffer
- buffer = null
- session.on 'message', handleMessage
+ buffer.push msg
session.on 'close', ->
return unless agent
@@ -20,7 +20,8 @@ module.exports = (model, options) ->
@connectTime = new Date
@headers = data.headers
@remoteAddress = data.remoteAddress
-
+ @authentication = data.authentication
+
# This is a map from docName -> listener function
@listeners = {}
View
@@ -1,16 +1,13 @@
-// Generated by CoffeeScript 1.3.3
(function() {
var Range, applyToShareJS;
-
Range = require("ace/range").Range;
-
applyToShareJS = function(editorDoc, delta, doc) {
var getStartOffsetPosition, pos, text;
getStartOffsetPosition = function(range) {
- var i, line, lines, offset, _i, _len;
+ var i, line, lines, offset, _len;
lines = editorDoc.getLines(0, range.start.row);
offset = 0;
- for (i = _i = 0, _len = lines.length; _i < _len; i = ++_i) {
+ for (i = 0, _len = lines.length; i < _len; i++) {
line = lines[i];
offset += i < range.start.row ? line.length : range.start.column;
}
@@ -36,7 +33,6 @@
throw new Error("unknown action: " + delta.action);
}
};
-
window.sharejs.extendDoc('attach_ace', function(editor, keepEditorContents) {
var check, doc, docListener, editorDoc, editorListener, offsetToPos, suppress;
if (!this.provides['text']) {
@@ -80,10 +76,10 @@
return check();
};
offsetToPos = function(offset) {
- var line, lines, row, _i, _len;
+ var line, lines, row, _len;
lines = editorDoc.getAllLines();
row = 0;
- for (row = _i = 0, _len = lines.length; _i < _len; row = ++_i) {
+ for (row = 0, _len = lines.length; row < _len; row++) {
line = lines[row];
if (offset <= line.length) {
break;
@@ -115,5 +111,4 @@
return delete doc.detach_ace;
};
});
-
}).call(this);
View
@@ -1,7 +1,5 @@
-// Generated by CoffeeScript 1.3.3
(function() {
var applyToShareJS;
-
applyToShareJS = function(editorDoc, delta, doc) {
var delLen, i, startPos;
startPos = 0;
@@ -28,7 +26,6 @@
return applyToShareJS(editorDoc, delta.next, doc);
}
};
-
window.sharejs.extendDoc('attach_cm', function(editor, keepEditorContents) {
var check, editorListener, sharedoc, suppress;
if (!this.provides.text) {
@@ -84,5 +81,4 @@
return delete this.detach_cm;
};
});
-
}).call(this);
View

Large diffs are not rendered by default.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit 8e49bcd

Please sign in to comment.