tentd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/Rakefile +8 -0
- data/bin/tent-server +3 -0
- data/lib/tentd.rb +31 -0
- data/lib/tentd/api.rb +58 -0
- data/lib/tentd/api/apps.rb +196 -0
- data/lib/tentd/api/authentication_finalize.rb +12 -0
- data/lib/tentd/api/authentication_lookup.rb +27 -0
- data/lib/tentd/api/authentication_verification.rb +50 -0
- data/lib/tentd/api/authorizable.rb +21 -0
- data/lib/tentd/api/authorization.rb +14 -0
- data/lib/tentd/api/core_profile_data.rb +45 -0
- data/lib/tentd/api/followers.rb +218 -0
- data/lib/tentd/api/followings.rb +241 -0
- data/lib/tentd/api/groups.rb +161 -0
- data/lib/tentd/api/middleware.rb +32 -0
- data/lib/tentd/api/posts.rb +373 -0
- data/lib/tentd/api/profile.rb +78 -0
- data/lib/tentd/api/router.rb +123 -0
- data/lib/tentd/api/router/caching_headers.rb +49 -0
- data/lib/tentd/api/router/extract_params.rb +88 -0
- data/lib/tentd/api/router/serialize_response.rb +38 -0
- data/lib/tentd/api/user_lookup.rb +10 -0
- data/lib/tentd/core_ext/hash/slice.rb +29 -0
- data/lib/tentd/datamapper/array_property.rb +23 -0
- data/lib/tentd/datamapper/query.rb +19 -0
- data/lib/tentd/json_patch.rb +181 -0
- data/lib/tentd/model.rb +30 -0
- data/lib/tentd/model/app.rb +68 -0
- data/lib/tentd/model/app_authorization.rb +113 -0
- data/lib/tentd/model/follower.rb +105 -0
- data/lib/tentd/model/following.rb +100 -0
- data/lib/tentd/model/group.rb +24 -0
- data/lib/tentd/model/mention.rb +19 -0
- data/lib/tentd/model/notification_subscription.rb +56 -0
- data/lib/tentd/model/permissible.rb +227 -0
- data/lib/tentd/model/permission.rb +28 -0
- data/lib/tentd/model/post.rb +178 -0
- data/lib/tentd/model/post_attachment.rb +27 -0
- data/lib/tentd/model/post_version.rb +64 -0
- data/lib/tentd/model/profile_info.rb +80 -0
- data/lib/tentd/model/random_public_id.rb +46 -0
- data/lib/tentd/model/serializable.rb +58 -0
- data/lib/tentd/model/type_properties.rb +36 -0
- data/lib/tentd/model/user.rb +39 -0
- data/lib/tentd/model/user_scoped.rb +14 -0
- data/lib/tentd/notifications.rb +13 -0
- data/lib/tentd/notifications/girl_friday.rb +30 -0
- data/lib/tentd/notifications/sidekiq.rb +50 -0
- data/lib/tentd/tent_type.rb +20 -0
- data/lib/tentd/tent_version.rb +41 -0
- data/lib/tentd/version.rb +3 -0
- data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
- data/spec/fabricators/apps_fabricator.rb +11 -0
- data/spec/fabricators/followers_fabricator.rb +14 -0
- data/spec/fabricators/followings_fabricator.rb +17 -0
- data/spec/fabricators/groups_fabricator.rb +3 -0
- data/spec/fabricators/mentions_fabricator.rb +3 -0
- data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
- data/spec/fabricators/permissions_fabricator.rb +1 -0
- data/spec/fabricators/post_attachments_fabricator.rb +8 -0
- data/spec/fabricators/post_versions_fabricator.rb +12 -0
- data/spec/fabricators/posts_fabricator.rb +12 -0
- data/spec/fabricators/profile_infos_fabricator.rb +30 -0
- data/spec/integration/api/apps_spec.rb +466 -0
- data/spec/integration/api/followers_spec.rb +535 -0
- data/spec/integration/api/followings_spec.rb +688 -0
- data/spec/integration/api/groups_spec.rb +207 -0
- data/spec/integration/api/posts_spec.rb +874 -0
- data/spec/integration/api/profile_spec.rb +285 -0
- data/spec/integration/api/router_spec.rb +102 -0
- data/spec/integration/model/app_authorization_spec.rb +59 -0
- data/spec/integration/model/app_spec.rb +63 -0
- data/spec/integration/model/follower_spec.rb +344 -0
- data/spec/integration/model/following_spec.rb +97 -0
- data/spec/integration/model/group_spec.rb +39 -0
- data/spec/integration/model/notification_subscription_spec.rb +145 -0
- data/spec/integration/model/post_spec.rb +658 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/expect_server.rb +3 -0
- data/spec/support/json_request.rb +54 -0
- data/spec/support/with_constant.rb +23 -0
- data/spec/support/with_warnings.rb +6 -0
- data/spec/unit/api/authentication_finalize_spec.rb +45 -0
- data/spec/unit/api/authentication_lookup_spec.rb +65 -0
- data/spec/unit/api/authentication_verification_spec.rb +50 -0
- data/spec/unit/api/authorizable_spec.rb +50 -0
- data/spec/unit/api/authorization_spec.rb +44 -0
- data/spec/unit/api/caching_headers_spec.rb +121 -0
- data/spec/unit/core_profile_data_spec.rb +64 -0
- data/spec/unit/json_patch_spec.rb +407 -0
- data/spec/unit/tent_type_spec.rb +28 -0
- data/spec/unit/tent_version_spec.rb +68 -0
- data/tentd.gemspec +36 -0
- metadata +435 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mocha_standalone'
|
6
|
+
require 'rack/test'
|
7
|
+
require 'tentd'
|
8
|
+
require 'fabrication'
|
9
|
+
require 'tentd/core_ext/hash/slice'
|
10
|
+
require 'girl_friday'
|
11
|
+
require 'tentd/notifications/girl_friday'
|
12
|
+
|
13
|
+
Dir["#{File.dirname(__FILE__)}/support/*.rb"].each { |f| require f }
|
14
|
+
|
15
|
+
ENV['RACK_ENV'] ||= 'test'
|
16
|
+
|
17
|
+
require 'data_mapper'
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include Rack::Test::Methods
|
21
|
+
config.include JsonRequest
|
22
|
+
config.mock_with :mocha
|
23
|
+
|
24
|
+
config.around do |suite|
|
25
|
+
with_constants "TentD::Notifications::NOTIFY_ENTITY_QUEUE" => [], "TentD::Notifications::TRIGGER_QUEUE" => [] do
|
26
|
+
suite.run
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
config.before(:suite) do
|
31
|
+
GirlFriday::WorkQueue.immediate!
|
32
|
+
# DataMapper::Logger.new(STDOUT, :debug)
|
33
|
+
DataMapper.setup(:default, ENV['TEST_DATABASE_URL'] || 'postgres://localhost/tent_server_test')
|
34
|
+
DataMapper.auto_migrate!
|
35
|
+
TentD::Model::User.current = TentD::Model::User.first_or_create
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module JsonRequest
|
4
|
+
def json_patch(path, data = {}, rack_env = {})
|
5
|
+
patch path, data.to_json, { 'CONTENT_TYPE' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
6
|
+
end
|
7
|
+
|
8
|
+
def json_put(path, data = {}, rack_env= {})
|
9
|
+
put path, data.to_json, { 'CONTENT_TYPE' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
10
|
+
end
|
11
|
+
|
12
|
+
def json_post(path, data = {}, rack_env = {})
|
13
|
+
post path, data.to_json, { 'CONTENT_TYPE' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
14
|
+
end
|
15
|
+
|
16
|
+
def json_get(path, data = {}, rack_env = {})
|
17
|
+
get path, data, { 'HTTP_ACCEPT' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def multipart_post(path, json, parts, rack_env = {})
|
21
|
+
body = build_json_part(json) + build_parts(parts) + "--#{Rack::Multipart::MULTIPART_BOUNDARY}--\r"
|
22
|
+
post path, body, { 'CONTENT_TYPE' => "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}",
|
23
|
+
'HTTP_ACCEPT' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
24
|
+
end
|
25
|
+
|
26
|
+
def multipart_put(path, json, parts, rack_env = {})
|
27
|
+
body = build_json_part(json) + build_parts(parts) + "--#{Rack::Multipart::MULTIPART_BOUNDARY}--\r"
|
28
|
+
put path, body, { 'CONTENT_TYPE' => "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}",
|
29
|
+
'HTTP_ACCEPT' => TentD::API::MEDIA_TYPE }.merge(rack_env)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def build_json_part(json)
|
35
|
+
build_part('post', :filename => 'post.json', :content_type => TentD::API::MEDIA_TYPE, :content => json.to_json)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_parts(parts)
|
39
|
+
parts.map do |k,v|
|
40
|
+
v.kind_of?(Array) ? v.each_with_index.map { |part,i| build_part("#{k}[#{i}]", part) } : build_part(k, v)
|
41
|
+
end.join
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_part(name, data)
|
45
|
+
<<-EOF
|
46
|
+
--#{Rack::Multipart::MULTIPART_BOUNDARY}\r
|
47
|
+
Content-Disposition: form-data; name="#{name}"; filename="#{Rack::Utils.escape(data[:filename])}"\r
|
48
|
+
Content-Type: #{data[:content_type]}\r
|
49
|
+
Content-Length: #{data[:content].size}\r
|
50
|
+
\r
|
51
|
+
#{data[:content]}\r
|
52
|
+
EOF
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
def split_constant_string(const_string)
|
2
|
+
parent_constant = const_string.to_s.split("::")[0..-2].inject(Object) { |o, c| o.const_get(c) }
|
3
|
+
child_constant = const_string.to_s.split("::").last
|
4
|
+
[parent_constant, child_constant]
|
5
|
+
end
|
6
|
+
|
7
|
+
def with_constants(constants, &block)
|
8
|
+
saved_constants = {}
|
9
|
+
constants.each_pair do |constant, val|
|
10
|
+
parent, child = split_constant_string(constant)
|
11
|
+
saved_constants[ constant ] = parent.const_get( child )
|
12
|
+
with_warnings(nil) { parent.const_set( child, val ) }
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
block.call
|
17
|
+
ensure
|
18
|
+
constants.each_pair do |constant, val|
|
19
|
+
parent, child = split_constant_string(constant)
|
20
|
+
with_warnings(nil) { parent.const_set( child, saved_constants[ constant ] ) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TentD::API::AuthenticationFinalize do
|
4
|
+
def app
|
5
|
+
TentD::API.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should set current_auth" do
|
9
|
+
instance = stub(:mac_timestamp_delta => 1346171050)
|
10
|
+
env = Hashie::Mash.new({
|
11
|
+
"potential_auth" => instance,
|
12
|
+
'hmac' => { 'ts' => "1336363200", 'verified' => true }
|
13
|
+
})
|
14
|
+
described_class.new(app).call(env)
|
15
|
+
expect(env["current_auth"]).to eq(instance)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should set mac_timestamp_delta on current_auth" do
|
19
|
+
now = Time.now; Time.stubs(:now).returns(now)
|
20
|
+
delta = now.to_i - 1336363200
|
21
|
+
instance = stub(:mac_timestamp_delta => nil)
|
22
|
+
env = Hashie::Mash.new({
|
23
|
+
"potential_auth" => instance,
|
24
|
+
'hmac' => { 'ts' => "1336363200", 'verified' => true }
|
25
|
+
})
|
26
|
+
instance.expects(:update).with(:mac_timestamp_delta => delta).returns(true)
|
27
|
+
described_class.new(app).call(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not set mac_timestamp_delta on current_auth if already set" do
|
31
|
+
instance = stub(:mac_timestamp_delta => 1346171050)
|
32
|
+
env = Hashie::Mash.new({
|
33
|
+
"potential_auth" => instance,
|
34
|
+
'hmac' => { 'ts' => "1336363200", 'verified' => true }
|
35
|
+
})
|
36
|
+
instance.expects(:update).never
|
37
|
+
described_class.new(app).call(env)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should do nothing unless env.hmac.verified present' do
|
41
|
+
env = Hashie::Mash.new({ "potential_auth" => stub(:mac_timestamp_delta => 1346171050) })
|
42
|
+
described_class.new(app).call(env)
|
43
|
+
expect(env.current_auth).to be_nil
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TentD::API::AuthenticationLookup do
|
4
|
+
def app
|
5
|
+
TentD::API.new
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:env) { Hashie::Mash.new({}) }
|
9
|
+
let(:auth_header) { 'MAC id="%s:h480djs93hd8", ts="1336363200", nonce="dj83hs9s", mac="hqpo01mLJLSYDbxmfRgNMEw38Wg="' }
|
10
|
+
|
11
|
+
it 'should parse hmac authorization header' do
|
12
|
+
TentD::Model::Follower.all.destroy!
|
13
|
+
follow = Fabricate(:follower, :mac_key_id => "s:h480djs93hd8")
|
14
|
+
env['HTTP_AUTHORIZATION'] = auth_header % 's'
|
15
|
+
described_class.new(app).call(env)
|
16
|
+
expect(env['hmac']).to eq({
|
17
|
+
"algorithm" => "hmac-sha-256",
|
18
|
+
"id" => "s:h480djs93hd8",
|
19
|
+
"ts" => "1336363200",
|
20
|
+
"nonce" => "dj83hs9s",
|
21
|
+
"secret" => follow.mac_key,
|
22
|
+
"mac" => "hqpo01mLJLSYDbxmfRgNMEw38Wg="
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should lookup server authentication model' do
|
27
|
+
TentD::Model::Follower.all(:mac_key_id => "s:h480djs93hd8").destroy!
|
28
|
+
follow = Fabricate(:follower, :mac_key_id => "s:h480djs93hd8")
|
29
|
+
expect(follow.saved?).to be_true
|
30
|
+
env['HTTP_AUTHORIZATION'] = auth_header % 's'
|
31
|
+
described_class.new(app).call(env)
|
32
|
+
expect(env.potential_auth).to eq(follow.reload)
|
33
|
+
expect(env.hmac.secret).to eq(follow.mac_key)
|
34
|
+
expect(env.hmac.algorithm).to eq(follow.mac_algorithm)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should lookup app authentication model' do
|
38
|
+
TentD::Model::App.all.destroy
|
39
|
+
authed_app = Fabricate(:app, :mac_key_id => "a:h480djs93hd8")
|
40
|
+
expect(authed_app.saved?).to be_true
|
41
|
+
env['HTTP_AUTHORIZATION'] = auth_header % 'a'
|
42
|
+
described_class.new(app).call(env)
|
43
|
+
expect(env.potential_auth).to eq(authed_app)
|
44
|
+
expect(env.hmac.secret).to eq(authed_app.mac_key)
|
45
|
+
expect(env.hmac.algorithm).to eq(authed_app.mac_algorithm)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should lookup user authentication model' do
|
49
|
+
TentD::Model::AppAuthorization.all.destroy!
|
50
|
+
authed_user = TentD::Model::AppAuthorization.create(:mac_key_id => "u:h480djs93hd8",
|
51
|
+
:app => Fabricate(:app))
|
52
|
+
expect(authed_user.saved?).to be_true
|
53
|
+
env['HTTP_AUTHORIZATION'] = auth_header % 'u'
|
54
|
+
described_class.new(app).call(env)
|
55
|
+
expect(env.potential_auth).to eq(authed_user)
|
56
|
+
expect(env.hmac.secret).to eq(authed_user.mac_key)
|
57
|
+
expect(env.hmac.algorithm).to eq(authed_user.mac_algorithm)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should do nothing unless HTTP_AUTHORIZATION header' do
|
61
|
+
env = {}
|
62
|
+
described_class.new(app).call(env)
|
63
|
+
expect(env['hmac']).to be_nil
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TentD::API::AuthenticationVerification do
|
4
|
+
def app
|
5
|
+
TentD::API.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should verify mac signature' do
|
9
|
+
env = Hashie::Mash.new({
|
10
|
+
'hmac' => {
|
11
|
+
"id" => "s:h480djs93hd8", "ts" => "1336363200", "nonce" => "dj83hs9s", "mac" => "hqpo01mLJLSYDbxmfRgNMEw38Wg=",
|
12
|
+
"secret" => '489dks293j39',
|
13
|
+
'algorithm' => 'hmac-sha-1',
|
14
|
+
},
|
15
|
+
'rack.input' => StringIO.new("asdf\nasdf"),
|
16
|
+
'REQUEST_METHOD' => 'POST',
|
17
|
+
'SCRIPT_NAME' => "/resource/1",
|
18
|
+
'QUERY_STRING' => "b=1&a=2",
|
19
|
+
'HTTP_HOST' => "example.com",
|
20
|
+
'SERVER_PORT' => "80"
|
21
|
+
})
|
22
|
+
described_class.new(app).call(env)
|
23
|
+
expect(env.hmac.verified).to be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should respond 403 Unauthorized if signature fails verification' do
|
27
|
+
env = Hashie::Mash.new({
|
28
|
+
'hmac' => {
|
29
|
+
"id" => "s:h480djs93hd8", "ts" => "1336363200", "nonce" => "dj83hs9s", "mac" => "hqpo01mLJLSYDbxmfRgNMEw38Wg=",
|
30
|
+
"secret" => 'WRONG-KEY',
|
31
|
+
'algorithm' => 'hmac-sha-1',
|
32
|
+
},
|
33
|
+
'rack.input' => StringIO.new("asdf\nasdf"),
|
34
|
+
'REQUEST_METHOD' => 'POST',
|
35
|
+
'SCRIPT_NAME' => "/resource/1",
|
36
|
+
'QUERY_STRING' => "b=1&a=2",
|
37
|
+
'HTTP_HOST' => "example.com",
|
38
|
+
'SERVER_PORT' => "80"
|
39
|
+
})
|
40
|
+
env = described_class.new(app).call(env)
|
41
|
+
expect(env).to be_an(Array)
|
42
|
+
expect(env.first).to eq(403)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should not do anything if no signature' do
|
46
|
+
env = Hashie::Mash.new({})
|
47
|
+
res = described_class.new(app).call(env)
|
48
|
+
expect(env.hmac).to be_nil
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hashie'
|
3
|
+
|
4
|
+
describe TentD::API::Authorizable do
|
5
|
+
class TestMiddleware2
|
6
|
+
include TentD::API::Authorizable
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
authorize_env!(env, :read_posts)
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class OtherTestMiddleware < TentD::API::Middleware
|
19
|
+
def action(env)
|
20
|
+
authorize_env!(env, :read_posts)
|
21
|
+
env
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def app
|
26
|
+
TentD::API.new
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:env) { Hashie::Mash.new }
|
30
|
+
let(:middleware) { TestMiddleware2.new(app) }
|
31
|
+
|
32
|
+
describe '#authorize_env!(env, scope)' do
|
33
|
+
it 'should raise Unauthorized unless env.authorized_scopes includes scope' do
|
34
|
+
expect( lambda { middleware.call(env) } ).to raise_error(described_class::Unauthorized)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should do nothing if env.authorized_scopes includes scope' do
|
38
|
+
env.authorized_scopes = [:read_posts]
|
39
|
+
expect( lambda { middleware.call(env) } ).to_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when TentD::API::Middleware' do
|
43
|
+
it 'should respond 403 unless env.authorized_scopes includes scope' do
|
44
|
+
response = OtherTestMiddleware.new(app).call(env)
|
45
|
+
expect(response).to be_an(Array)
|
46
|
+
expect(response.first).to be(403)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hashie'
|
3
|
+
|
4
|
+
describe TentD::API::Authorization do
|
5
|
+
def app
|
6
|
+
TentD::API.new
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:env) { Hashie::Mash.new }
|
10
|
+
|
11
|
+
not_authorized = proc do
|
12
|
+
it 'should not authorize scopes' do
|
13
|
+
described_class.new(app).call(env)
|
14
|
+
expect(env.authorized_scopes).to eq([])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'without current_auth', ¬_authorized
|
19
|
+
|
20
|
+
context 'with current_auth' do
|
21
|
+
context 'when Follower' do
|
22
|
+
before do
|
23
|
+
env.current_auth = Fabricate(:follower)
|
24
|
+
end
|
25
|
+
|
26
|
+
context '', ¬_authorized
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when AppAuthorization' do
|
30
|
+
before do
|
31
|
+
env.current_auth = Fabricate(
|
32
|
+
:app_authorization,
|
33
|
+
:scopes => ['read_posts', 'write_posts'],
|
34
|
+
:app => Fabricate(:app)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should lookup authorized scopes' do
|
39
|
+
described_class.new(app).call(env)
|
40
|
+
expect(env.authorized_scopes).to eq([:read_posts, :write_posts])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TentD::API::Router::CachingHeaders do
|
4
|
+
let(:app) { lambda { |env| [200, {}, ''] } }
|
5
|
+
let(:middleware) { TentD::API::Router::CachingHeaders.new(app) }
|
6
|
+
let(:env) { Hashie::Mash.new('REQUEST_METHOD' => 'GET') }
|
7
|
+
|
8
|
+
shared_examples 'conditional get' do
|
9
|
+
it 'should respond with a 304 when cached' do
|
10
|
+
status, headers, body = middleware.call env.merge('response' => response,
|
11
|
+
'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate)
|
12
|
+
expect(status).to eq(304)
|
13
|
+
expect(body).to be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should not 304 when not cached' do
|
17
|
+
status, headers, body = middleware.call env.merge('response' => response,
|
18
|
+
'HTTP_IF_MODIFIED_SINCE' => (Time.now - 60).httpdate)
|
19
|
+
expect(status).to eq(200)
|
20
|
+
expect(headers['Last-Modified']).to_not be_nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
shared_examples 'public response' do
|
25
|
+
it 'should set Cache-Control to public' do
|
26
|
+
expect(middleware.call(env.merge('response' => response))[1]['Cache-Control']).to eq('public')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
shared_examples 'private response' do
|
31
|
+
it 'should set Cache-Control to private' do
|
32
|
+
expect(middleware.call(env.merge('response' => response))[1]['Cache-Control']).to eq('private')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'object instance with #updated_at' do
|
37
|
+
let(:response) { stub(:updated_at => Time.now-1) }
|
38
|
+
it_behaves_like 'conditional get'
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'hash with "updated_at"' do
|
42
|
+
let(:response) { { 'updated_at' => Time.now-1 } }
|
43
|
+
it_behaves_like 'conditional get'
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'object array with #updated_at' do
|
47
|
+
let(:response) { [stub(:updated_at => Time.now-1), stub(:updated_at => Time.now-90)] }
|
48
|
+
it_behaves_like 'conditional get'
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'hash array with "updated_at"' do
|
52
|
+
let(:response) { [{ "updated_at" => Time.now-1 }, { "updated_at" => Time.now-90 }] }
|
53
|
+
it_behaves_like 'conditional get'
|
54
|
+
end
|
55
|
+
|
56
|
+
context "object that doesn't respond to #updated_at" do
|
57
|
+
it "should return without changes" do
|
58
|
+
expect(middleware.call(env.merge('response' => stub)).first).to eq(200)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "object that is #public" do
|
63
|
+
let(:response) { stub(:public => true) }
|
64
|
+
it_behaves_like 'public response'
|
65
|
+
end
|
66
|
+
|
67
|
+
context "object that is not #public" do
|
68
|
+
let(:response) { stub(:public => false) }
|
69
|
+
it_behaves_like 'private response'
|
70
|
+
end
|
71
|
+
|
72
|
+
context "array of #public objects" do
|
73
|
+
let(:response) { [stub(:public => true)] * 2 }
|
74
|
+
it_behaves_like 'public response'
|
75
|
+
end
|
76
|
+
|
77
|
+
context "array of public and private objects" do
|
78
|
+
let(:response) { [stub(:public => true), stub(:public => false)] }
|
79
|
+
it_behaves_like 'private response'
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'hash that is "public"' do
|
83
|
+
let(:response) { { "public" => true } }
|
84
|
+
it_behaves_like 'public response'
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'hash that is not "public"' do
|
88
|
+
let(:response) { { "public" => false } }
|
89
|
+
it_behaves_like 'private response'
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'array of hashes that are "public"' do
|
93
|
+
let(:response) { [{ "public" => true }]*2 }
|
94
|
+
it_behaves_like 'public response'
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'array of hashes that are not "public"' do
|
98
|
+
let(:response) { [{ "public" => false }]*2 }
|
99
|
+
it_behaves_like 'private response'
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'hash that has public set on permissions' do
|
103
|
+
let(:response) { { "permissions" => { "public" => true } } }
|
104
|
+
it_behaves_like 'public response'
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'hash that has public set to false on permissions' do
|
108
|
+
let(:response) { { "permissions" => { "public" => false } } }
|
109
|
+
it_behaves_like 'private response'
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'array of hashes that have public set on permissions' do
|
113
|
+
let(:response) { [{ "permissions" => { "public" => true } }]*2 }
|
114
|
+
it_behaves_like 'public response'
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'array of hashes that have public set to false on permissions' do
|
118
|
+
let(:response) { [{ "permissions" => { "public" => false } }]*2 }
|
119
|
+
it_behaves_like 'private response'
|
120
|
+
end
|
121
|
+
end
|