faye-authentication 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +9 -0
- data/app/assets/javascripts/faye-authentication.js +52 -0
- data/faye-authentication.gemspec +29 -0
- data/lib/faye/authentication/engine.rb +8 -0
- data/lib/faye/authentication/extension.rb +24 -0
- data/lib/faye/authentication/http_client.rb +18 -0
- data/lib/faye/authentication/version.rb +5 -0
- data/lib/faye/authentication.rb +32 -0
- data/spec/javascripts/faye-authentication_spec.js +153 -0
- data/spec/javascripts/faye-extension_spec.js +74 -0
- data/spec/javascripts/helpers/.gitkeep +0 -0
- data/spec/javascripts/support/jasmine.yml +124 -0
- data/spec/javascripts/support/jasmine_helper.rb +29 -0
- data/spec/lib/faye/authentication/extension_spec.rb +45 -0
- data/spec/lib/faye/authentication/http_client_spec.rb +18 -0
- data/spec/lib/faye/authentication_spec.rb +24 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/utils/javascripts/core.js +712 -0
- data/spec/utils/javascripts/faye.js +2541 -0
- data/spec/utils/javascripts/hmac.js +131 -0
- data/spec/utils/javascripts/jquery.js +4 -0
- data/spec/utils/javascripts/mock-ajax.js +282 -0
- data/spec/utils/javascripts/sha1.js +136 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cfd708e9dbb1900470d238f7d427ba3a6bd4322c
|
4
|
+
data.tar.gz: 5d087943cf31d1ffe01f645039d8c9c034fdeacd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7fa639d27c2ce7ecd9876355b241cb3db5c490f8082db95c156410f16bc3ee5790bc88020a2d4055c2a4f341569e3696299794bb52e672514d16390c2f0a5e93
|
7
|
+
data.tar.gz: 4f2d0c80f71e1defc714a0a76da7472390dfb19b300b469c3d71d1f38189b418364912fa3988da35048598db584f55ea0bc6bc6891e464a9f632567e4229e85b
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.DS_Store
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Adrien Siami
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Faye::Authentication [](https://travis-ci.org/josevalim/rails-footnotes) [](https://codeclimate.com/github/dimelo/faye-authentication)
|
2
|
+
|
3
|
+
Authentification implementation for faye
|
4
|
+
|
5
|
+
Currently Implemented :
|
6
|
+
- Javascript Client Extention
|
7
|
+
- Ruby Server Extension
|
8
|
+
- Ruby utils for signing messages
|
9
|
+
- **Want another one ? Pull requests are welcome.**
|
10
|
+
|
11
|
+
The authentication is performed through an Ajax Call to the webserver (JQuery needed).
|
12
|
+
|
13
|
+
For each channel and client id pair, a signature is added to the message.
|
14
|
+
|
15
|
+
Thanks to a shared key, the Faye Server will check the signature and reject the
|
16
|
+
message if the signature is incorrect or not present.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'faye-authentication'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install faye-authentication
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
### Javascript client extension
|
35
|
+
|
36
|
+
Add the extension to your faye client :
|
37
|
+
|
38
|
+
````javascript
|
39
|
+
var client = new Faye.Client('http://my.server/faye');
|
40
|
+
client.add_extension(new FayeAuthentication());
|
41
|
+
````
|
42
|
+
|
43
|
+
By default, when sending a subscribe request or publishing a message, the extension
|
44
|
+
will issue an AJAX request to ``/faye/auth``
|
45
|
+
|
46
|
+
If you wish to change the endpoint, you can supply it as the first argument of the extension constructor :
|
47
|
+
|
48
|
+
client.add_extension(new FayeAuthentication('/my_custom_auth_endpoint'));
|
49
|
+
|
50
|
+
|
51
|
+
### Ruby utils
|
52
|
+
|
53
|
+
The endpoint will a POST request, and shall return a JSON hash with a ``signature`` key.
|
54
|
+
|
55
|
+
The parameters sent to the endpoint are the following :
|
56
|
+
|
57
|
+
````
|
58
|
+
{
|
59
|
+
'message' =>
|
60
|
+
{
|
61
|
+
'channel' => '/foo/bar',
|
62
|
+
'clientId' => '123abc'
|
63
|
+
}
|
64
|
+
}
|
65
|
+
````
|
66
|
+
|
67
|
+
If the endpoint returns an error, the message won't be signed and the server will reject it.
|
68
|
+
|
69
|
+
You can use ``Faye::Authentication.sign`` to generate the signature from the message and a private key.
|
70
|
+
|
71
|
+
Example (For a Rails application)
|
72
|
+
|
73
|
+
````ruby
|
74
|
+
def auth
|
75
|
+
if current_user.can?(:read, params[:message][:channel])
|
76
|
+
render json: {signature: Faye::Authentication.sign(params[:message], 'your private key')}
|
77
|
+
else
|
78
|
+
render json: {error: 'Not authorized'}, status: 403
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
````
|
83
|
+
|
84
|
+
A Ruby HTTP Client is also available for publishing messages to your faye server
|
85
|
+
without the hassle of using EventMachine :
|
86
|
+
|
87
|
+
````ruby
|
88
|
+
Faye::Authentication::HTTPClient.publish('/channel', 'data', 'your private key')
|
89
|
+
````
|
90
|
+
|
91
|
+
### Faye server extension
|
92
|
+
|
93
|
+
Instanciate the extension with your secret key and add it to the server :
|
94
|
+
|
95
|
+
````ruby
|
96
|
+
server = Faye::RackAdapter.new(:mount => '/faye', :timeout => 15)
|
97
|
+
server.add_extension Faye::Authentication::Extension.new('your private key')
|
98
|
+
````
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it ( https://github.com/dimelo/faye-authentication/fork )
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
function FayeAuthentication(endpoint) {
|
2
|
+
this._endpoint = endpoint || '/faye/auth';
|
3
|
+
this._signatures = {};
|
4
|
+
}
|
5
|
+
|
6
|
+
FayeAuthentication.prototype.endpoint = function() {
|
7
|
+
return (this._endpoint);
|
8
|
+
};
|
9
|
+
|
10
|
+
FayeAuthentication.prototype.signMessage = function(message, callback) {
|
11
|
+
var channel = message.subscription || message.channel;
|
12
|
+
var clientId = message.clientId;
|
13
|
+
|
14
|
+
if (!this._signatures[clientId])
|
15
|
+
this._signatures[clientId] = {};
|
16
|
+
if (this._signatures[clientId][channel]) {
|
17
|
+
this._signatures[clientId][channel].then(function(signature) {
|
18
|
+
message.signature = signature;
|
19
|
+
callback(message);
|
20
|
+
});
|
21
|
+
} else {
|
22
|
+
var self = this;
|
23
|
+
self._signatures[clientId][channel] = new Faye.Promise(function(success, failure) {
|
24
|
+
$.post(self.endpoint(), {message: {channel: channel, clientId: clientId}}, function(response) {
|
25
|
+
success(response.signature);
|
26
|
+
}, 'json').fail(function(xhr, textStatus, e) {
|
27
|
+
success(null);
|
28
|
+
});
|
29
|
+
});
|
30
|
+
self._signatures[clientId][channel].then(function(signature) {
|
31
|
+
message.signature = signature;
|
32
|
+
callback(message);
|
33
|
+
});
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
FayeAuthentication.prototype.outgoing = function(message, callback) {
|
38
|
+
if (message.channel === '/meta/subscribe') {
|
39
|
+
this.signMessage(message, callback);
|
40
|
+
}
|
41
|
+
else if (/^\/meta\/(.*)/.exec(message.channel) === null) { // Publish
|
42
|
+
this.signMessage(message, callback);
|
43
|
+
}
|
44
|
+
else
|
45
|
+
callback(message);
|
46
|
+
};
|
47
|
+
|
48
|
+
FayeAuthentication.prototype.incoming = function(message, callback) {
|
49
|
+
if (message.error === 'Invalid signature')
|
50
|
+
this._signatures = {};
|
51
|
+
callback(message);
|
52
|
+
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'faye/authentication/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "faye-authentication"
|
8
|
+
spec.version = Faye::Authentication::VERSION
|
9
|
+
spec.authors = ["Adrien Siami"]
|
10
|
+
spec.email = ["adrien.siami@dimelo.com"]
|
11
|
+
spec.summary =
|
12
|
+
spec.description = "A faye extension to add authentication mechanisms"
|
13
|
+
spec.homepage = "https://github.com/dimelo/faye-authentication"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake", '~> 10.3'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0.rc1'
|
24
|
+
spec.add_development_dependency 'jasmine', '~> 2.0'
|
25
|
+
spec.add_development_dependency 'faye', '~> 1.0'
|
26
|
+
spec.add_development_dependency 'rack', '~> 1.5'
|
27
|
+
spec.add_development_dependency 'thin', '~> 1.6'
|
28
|
+
spec.add_development_dependency 'webmock', '~> 1.18'
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Faye
|
2
|
+
module Authentication
|
3
|
+
class Extension
|
4
|
+
|
5
|
+
def initialize(secret)
|
6
|
+
@secret = secret
|
7
|
+
end
|
8
|
+
|
9
|
+
def incoming(message, callback)
|
10
|
+
if message['channel'] == '/meta/subscribe' || !(message['channel'] =~ /^\/meta\/.*/)
|
11
|
+
unless Faye::Authentication.valid?({
|
12
|
+
'channel' => message['subscription'] || message['channel'],
|
13
|
+
'clientId' => message['clientId'],
|
14
|
+
'signature' => message['signature']
|
15
|
+
}, @secret)
|
16
|
+
message['error'] = 'Invalid signature'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
callback.call(message)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Faye
|
4
|
+
module Authentication
|
5
|
+
class HTTPClient
|
6
|
+
|
7
|
+
def self.publish(url, channel, data, key)
|
8
|
+
uri = URI(url)
|
9
|
+
req = Net::HTTP::Post.new(url)
|
10
|
+
message = {'channel' => channel, 'data' => data, 'clientId' => 'http'}
|
11
|
+
message['signature'] = Faye::Authentication.sign(message, key)
|
12
|
+
req.set_form_data(message: JSON.dump(message))
|
13
|
+
Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') { |http| http.request(req) }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "faye/authentication/version"
|
2
|
+
require 'faye/authentication/extension'
|
3
|
+
require 'faye/authentication/http_client'
|
4
|
+
require 'faye/authentication/engine'
|
5
|
+
|
6
|
+
module Faye
|
7
|
+
module Authentication
|
8
|
+
|
9
|
+
def self.sign(message, secret)
|
10
|
+
OpenSSL::HMAC.hexdigest('sha1', secret, "#{message['channel']}-#{message['clientId']}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.valid?(message, secret)
|
14
|
+
signature = message.delete('signature')
|
15
|
+
return false unless signature
|
16
|
+
secure_compare(signature, sign(message, secret))
|
17
|
+
end
|
18
|
+
|
19
|
+
# constant-time comparison algorithm to prevent timing attacks
|
20
|
+
# Copied from ActiveSupport::MessageVerifier
|
21
|
+
def self.secure_compare(a, b)
|
22
|
+
return false unless a.bytesize == b.bytesize
|
23
|
+
|
24
|
+
l = a.unpack "C#{a.bytesize}"
|
25
|
+
|
26
|
+
res = 0
|
27
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
28
|
+
res == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
describe('faye-authentication', function() {
|
2
|
+
|
3
|
+
describe('constructor', function() {
|
4
|
+
|
5
|
+
it('sets endpoint to /faye by default', function() {
|
6
|
+
var auth = new FayeAuthentication();
|
7
|
+
expect(auth.endpoint()).toBe('/faye/auth');
|
8
|
+
});
|
9
|
+
|
10
|
+
it('can specify a custom endpoint', function() {
|
11
|
+
var auth = new FayeAuthentication('/custom');
|
12
|
+
expect(auth.endpoint()).toBe('/custom');
|
13
|
+
});
|
14
|
+
|
15
|
+
});
|
16
|
+
|
17
|
+
describe('extension', function() {
|
18
|
+
beforeEach(function() {
|
19
|
+
jasmine.Ajax.install();
|
20
|
+
this.auth = new FayeAuthentication();
|
21
|
+
this.client = new Faye.Client('http://localhost:9296/faye');
|
22
|
+
this.client.addExtension(this.auth);
|
23
|
+
});
|
24
|
+
|
25
|
+
afterEach(function() {
|
26
|
+
jasmine.Ajax.uninstall();
|
27
|
+
});
|
28
|
+
|
29
|
+
it('should make an ajax request to the extension endpoint', function(done) {
|
30
|
+
this.client.subscribe('/foobar');
|
31
|
+
var self = this;
|
32
|
+
setTimeout(function() {
|
33
|
+
var request = jasmine.Ajax.requests.mostRecent();
|
34
|
+
expect(request.url).toBe(self.auth.endpoint());
|
35
|
+
done();
|
36
|
+
}, 500);
|
37
|
+
});
|
38
|
+
|
39
|
+
it('should make an ajax request with the correct params', function(done) {
|
40
|
+
this.client.subscribe('/foobar');
|
41
|
+
var self = this;
|
42
|
+
setTimeout(function() {
|
43
|
+
var request = jasmine.Ajax.requests.mostRecent();
|
44
|
+
console.log(request);
|
45
|
+
expect(request.data()['message[channel]'][0]).toBe('/foobar');
|
46
|
+
done();
|
47
|
+
}, 500);
|
48
|
+
});
|
49
|
+
|
50
|
+
|
51
|
+
describe('signature', function() {
|
52
|
+
|
53
|
+
beforeEach(function() {
|
54
|
+
jasmine.Ajax.stubRequest('/faye/auth').andReturn({
|
55
|
+
'responseText': '{"signature": "foobarsignature"}'
|
56
|
+
});
|
57
|
+
|
58
|
+
this.fake_transport = {connectionType: "fake", endpoint: {}, send: function() {}};
|
59
|
+
spyOn(this.fake_transport, 'send');
|
60
|
+
});
|
61
|
+
|
62
|
+
it('should add the signature to subscribe message', function(done) {
|
63
|
+
var self = this;
|
64
|
+
|
65
|
+
this.client.handshake(function() {
|
66
|
+
self.client._transport = self.fake_transport
|
67
|
+
self.client.subscribe('/foobar');
|
68
|
+
}, this.client);
|
69
|
+
|
70
|
+
setTimeout(function() {
|
71
|
+
var calls = self.fake_transport.send.calls.all();
|
72
|
+
var last_call = calls[calls.length - 1];
|
73
|
+
var message = last_call.args[0].message;
|
74
|
+
expect(message.channel).toBe('/meta/subscribe');
|
75
|
+
expect(message.signature).toBe('foobarsignature');
|
76
|
+
done();
|
77
|
+
}, 500);
|
78
|
+
|
79
|
+
});
|
80
|
+
|
81
|
+
it('should add the signature to publish message', function(done) {
|
82
|
+
var self = this;
|
83
|
+
|
84
|
+
this.client.handshake(function() {
|
85
|
+
self.client._transport = self.fake_transport
|
86
|
+
self.client.publish('/foobar', {text: 'hallo'});
|
87
|
+
}, this.client);
|
88
|
+
|
89
|
+
setTimeout(function() {
|
90
|
+
var calls = self.fake_transport.send.calls.all();
|
91
|
+
var last_call = calls[calls.length - 1];
|
92
|
+
var message = last_call.args[0].message;
|
93
|
+
expect(message.channel).toBe('/foobar');
|
94
|
+
expect(message.signature).toBe('foobarsignature');
|
95
|
+
done();
|
96
|
+
}, 500);
|
97
|
+
});
|
98
|
+
|
99
|
+
it('preserves messages integrity', function(done) {
|
100
|
+
var self = this;
|
101
|
+
|
102
|
+
this.client.handshake(function() {
|
103
|
+
self.client._transport = self.fake_transport
|
104
|
+
self.client.publish('/foo', {text: 'hallo'});
|
105
|
+
self.client.subscribe('/foo');
|
106
|
+
}, this.client);
|
107
|
+
|
108
|
+
setTimeout(function() {
|
109
|
+
var calls = self.fake_transport.send.calls.all();
|
110
|
+
var subscribe_call = calls[calls.length - 1];
|
111
|
+
var publish_call = calls[calls.length - 2];
|
112
|
+
var subscribe_message = subscribe_call.args[0].message;
|
113
|
+
var publish_message = publish_call.args[0].message;
|
114
|
+
expect(publish_message.channel).toBe('/foo');
|
115
|
+
expect(subscribe_message.channel).toBe('/meta/subscribe');
|
116
|
+
expect(publish_message.signature).toBe('foobarsignature');
|
117
|
+
expect(subscribe_message.signature).toBe('foobarsignature');
|
118
|
+
done();
|
119
|
+
}, 500);
|
120
|
+
});
|
121
|
+
|
122
|
+
it('should make only one ajax call when dealing with one channel', function(done) {
|
123
|
+
this.client.subscribe('/foobar');
|
124
|
+
this.client.publish('/foobar', {text: 'hallo'});
|
125
|
+
this.client.publish('/foobar', {text: 'hallo'});
|
126
|
+
|
127
|
+
setTimeout(function() {
|
128
|
+
expect(jasmine.Ajax.requests.count()).toBe(2); // Handshake + auth
|
129
|
+
done();
|
130
|
+
}, 500);
|
131
|
+
|
132
|
+
})
|
133
|
+
|
134
|
+
it('should make two ajax calls when dealing with two channels', function(done) {
|
135
|
+
this.client.subscribe('/foo');
|
136
|
+
this.client.publish('/foo', {text: 'hallo'});
|
137
|
+
this.client.publish('/foo', {text: 'hallo'});
|
138
|
+
|
139
|
+
this.client.subscribe('/bar');
|
140
|
+
this.client.publish('/bar', {text: 'hallo'});
|
141
|
+
this.client.publish('/bar', {text: 'hallo'});
|
142
|
+
|
143
|
+
setTimeout(function() {
|
144
|
+
expect(jasmine.Ajax.requests.count()).toBe(3); // Handshake + auth * 2
|
145
|
+
done();
|
146
|
+
}, 500);
|
147
|
+
});
|
148
|
+
});
|
149
|
+
|
150
|
+
});
|
151
|
+
|
152
|
+
|
153
|
+
})
|
@@ -0,0 +1,74 @@
|
|
1
|
+
describe('Faye extension', function() {
|
2
|
+
beforeEach(function() {
|
3
|
+
this.client = new Faye.Client('http://localhost:9296/faye');
|
4
|
+
jasmine.Ajax.install();
|
5
|
+
});
|
6
|
+
|
7
|
+
afterEach(function() {
|
8
|
+
jasmine.Ajax.uninstall();
|
9
|
+
});
|
10
|
+
|
11
|
+
describe('Without extension', function() {
|
12
|
+
it('fails to subscribe', function(done) {
|
13
|
+
this.client.subscribe('/foobar').then(undefined, function() {
|
14
|
+
done();
|
15
|
+
});
|
16
|
+
});
|
17
|
+
|
18
|
+
it('fails to publish', function(done) {
|
19
|
+
this.client.publish('/foobar', {text: 'whatever'}).then(undefined, function() {
|
20
|
+
done();
|
21
|
+
});
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
describe('With extension', function() {
|
26
|
+
beforeEach(function() {
|
27
|
+
this.extension = new FayeAuthentication();
|
28
|
+
this.client.addExtension(this.extension);
|
29
|
+
});
|
30
|
+
|
31
|
+
function stubSignature(context, callback) {
|
32
|
+
var self = context;
|
33
|
+
self.client.handshake(function() {
|
34
|
+
var signature = CryptoJS.HmacSHA1("/foobar-" + self.client._clientId, "macaroni").toString();
|
35
|
+
|
36
|
+
jasmine.Ajax.stubRequest('/faye/auth').andReturn({
|
37
|
+
'responseText': '{"signature": "' + signature + '"}'
|
38
|
+
});
|
39
|
+
callback();
|
40
|
+
}, self.client);
|
41
|
+
}
|
42
|
+
|
43
|
+
it('succeeds to subscribe', function(done) {
|
44
|
+
var self = this;
|
45
|
+
stubSignature(this, function() {
|
46
|
+
self.client.subscribe('/foobar').then(function() {
|
47
|
+
done();
|
48
|
+
});
|
49
|
+
});
|
50
|
+
});
|
51
|
+
|
52
|
+
it('succeeds to publish', function(done) {
|
53
|
+
var self = this;
|
54
|
+
stubSignature(this, function() {
|
55
|
+
self.client.publish('/foobar', {hello: 'world'}).then(function() {
|
56
|
+
done();
|
57
|
+
});
|
58
|
+
});
|
59
|
+
});
|
60
|
+
|
61
|
+
it('clears the signatures when receiving an error from the server', function(done) {
|
62
|
+
this.extension._signatures = {'123': []};
|
63
|
+
jasmine.Ajax.stubRequest('/faye/auth').andReturn({
|
64
|
+
'responseText': '{"signature": "bad"}'
|
65
|
+
});
|
66
|
+
var self = this;
|
67
|
+
this.client.subscribe('/toto').then(undefined, function() {
|
68
|
+
expect(Object.keys(self.extension._signatures).length).toBe(0);
|
69
|
+
done();
|
70
|
+
});
|
71
|
+
|
72
|
+
});
|
73
|
+
});
|
74
|
+
});
|
File without changes
|