sinatra_resource 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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.mdown +28 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/examples/datacatalog/Rakefile +23 -0
- data/examples/datacatalog/app.rb +15 -0
- data/examples/datacatalog/config/config.rb +66 -0
- data/examples/datacatalog/config/config.yml +11 -0
- data/examples/datacatalog/config.ru +6 -0
- data/examples/datacatalog/lib/base.rb +9 -0
- data/examples/datacatalog/lib/resource.rb +73 -0
- data/examples/datacatalog/lib/roles.rb +15 -0
- data/examples/datacatalog/models/categorization.rb +31 -0
- data/examples/datacatalog/models/category.rb +28 -0
- data/examples/datacatalog/models/source.rb +33 -0
- data/examples/datacatalog/models/user.rb +51 -0
- data/examples/datacatalog/resources/categories.rb +32 -0
- data/examples/datacatalog/resources/sources.rb +35 -0
- data/examples/datacatalog/resources/users.rb +26 -0
- data/examples/datacatalog/tasks/db.rake +29 -0
- data/examples/datacatalog/tasks/test.rake +16 -0
- data/examples/datacatalog/test/helpers/assertions/assert_include.rb +17 -0
- data/examples/datacatalog/test/helpers/assertions/assert_not_include.rb +17 -0
- data/examples/datacatalog/test/helpers/lib/model_factories.rb +49 -0
- data/examples/datacatalog/test/helpers/lib/model_helpers.rb +30 -0
- data/examples/datacatalog/test/helpers/lib/request_helpers.rb +53 -0
- data/examples/datacatalog/test/helpers/model_test_helper.rb +5 -0
- data/examples/datacatalog/test/helpers/resource_test_helper.rb +5 -0
- data/examples/datacatalog/test/helpers/shared/api_keys.rb +48 -0
- data/examples/datacatalog/test/helpers/shared/common_body_responses.rb +15 -0
- data/examples/datacatalog/test/helpers/shared/status_codes.rb +61 -0
- data/examples/datacatalog/test/helpers/test_cases/model_test_case.rb +6 -0
- data/examples/datacatalog/test/helpers/test_cases/resource_test_case.rb +36 -0
- data/examples/datacatalog/test/helpers/test_helper.rb +36 -0
- data/examples/datacatalog/test/models/categorization_test.rb +40 -0
- data/examples/datacatalog/test/models/category_test.rb +35 -0
- data/examples/datacatalog/test/models/source_test.rb +37 -0
- data/examples/datacatalog/test/models/user_test.rb +77 -0
- data/examples/datacatalog/test/resources/categories/categories_delete_test.rb +112 -0
- data/examples/datacatalog/test/resources/categories/categories_get_many_test.rb +58 -0
- data/examples/datacatalog/test/resources/categories/categories_get_one_test.rb +75 -0
- data/examples/datacatalog/test/resources/categories/categories_post_test.rb +135 -0
- data/examples/datacatalog/test/resources/categories/categories_put_test.rb +140 -0
- data/examples/datacatalog/test/resources/sources/sources_delete_test.rb +112 -0
- data/examples/datacatalog/test/resources/sources/sources_get_many_test.rb +58 -0
- data/examples/datacatalog/test/resources/sources/sources_get_one_test.rb +74 -0
- data/examples/datacatalog/test/resources/sources/sources_post_test.rb +184 -0
- data/examples/datacatalog/test/resources/sources/sources_put_test.rb +227 -0
- data/examples/datacatalog/test/resources/users/users_delete_test.rb +134 -0
- data/examples/datacatalog/test/resources/users/users_get_many_test.rb +111 -0
- data/examples/datacatalog/test/resources/users/users_get_one_test.rb +75 -0
- data/examples/datacatalog/test/resources/users/users_post_test.rb +142 -0
- data/examples/datacatalog/test/resources/users/users_put_test.rb +171 -0
- data/lib/builder/helpers.rb +319 -0
- data/lib/builder/mongo_helpers.rb +70 -0
- data/lib/builder.rb +84 -0
- data/lib/exceptions.rb +10 -0
- data/lib/resource.rb +171 -0
- data/lib/roles.rb +163 -0
- data/lib/sinatra_resource.rb +6 -0
- data/notes/keywords.mdown +1 -0
- data/notes/permissions.mdown +181 -0
- data/notes/questions.mdown +18 -0
- data/notes/see_also.mdown +3 -0
- data/notes/synonyms.mdown +7 -0
- data/notes/to_do.mdown +7 -0
- data/notes/uniform_interface.mdown +22 -0
- data/sinatra_resource.gemspec +183 -0
- data/spec/sinatra_resource_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/spec.rake +13 -0
- data/tasks/yard.rake +13 -0
- metadata +253 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
|
2
|
+
|
3
|
+
class UsersGetOneResourceTest < ResourceTestCase
|
4
|
+
|
5
|
+
def app; DataCatalog::Users end
|
6
|
+
|
7
|
+
before do
|
8
|
+
@user = create_user
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
@user.destroy
|
13
|
+
end
|
14
|
+
|
15
|
+
context "get /:id" do
|
16
|
+
context "anonymous" do
|
17
|
+
before do
|
18
|
+
get "/#{@user.id}"
|
19
|
+
end
|
20
|
+
|
21
|
+
use "return 401 because the API key is missing"
|
22
|
+
end
|
23
|
+
|
24
|
+
context "incorrect API key" do
|
25
|
+
before do
|
26
|
+
get "/#{@user.id}", :api_key => BAD_API_KEY
|
27
|
+
end
|
28
|
+
|
29
|
+
use "return 401 because the API key is invalid"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
%w(basic curator admin).each do |role|
|
34
|
+
context "#{role} : get /:fake_id" do
|
35
|
+
before do
|
36
|
+
get "/#{FAKE_ID}", :api_key => api_key_for(role)
|
37
|
+
end
|
38
|
+
|
39
|
+
use "return 404 Not Found"
|
40
|
+
use "return an empty response body"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
%w(basic curator).each do |role|
|
45
|
+
context "#{role} : get /:id" do
|
46
|
+
before do
|
47
|
+
get "/#{@user.id}", :api_key => api_key_for(role)
|
48
|
+
end
|
49
|
+
|
50
|
+
use "return 200 Ok"
|
51
|
+
doc_properties %w(name id created_at updated_at)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "owner : get /:id" do
|
56
|
+
before do
|
57
|
+
get "/#{@user.id}", :api_key => @user._api_key
|
58
|
+
end
|
59
|
+
|
60
|
+
use "return 200 Ok"
|
61
|
+
doc_properties %w(name email role _api_key id created_at updated_at)
|
62
|
+
end
|
63
|
+
|
64
|
+
%w(admin).each do |role|
|
65
|
+
context "#{role} : get /:id" do
|
66
|
+
before do
|
67
|
+
get "/#{@user.id}", :api_key => api_key_for(role)
|
68
|
+
end
|
69
|
+
|
70
|
+
use "return 200 Ok"
|
71
|
+
doc_properties %w(name email role _api_key id created_at updated_at)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
|
2
|
+
|
3
|
+
class UsersPostResourceTest < ResourceTestCase
|
4
|
+
|
5
|
+
include DataCatalog
|
6
|
+
|
7
|
+
def app; Users end
|
8
|
+
|
9
|
+
before do
|
10
|
+
@user_count = User.all.length
|
11
|
+
@valid_params = {
|
12
|
+
:name => "New User",
|
13
|
+
:role => "basic"
|
14
|
+
}
|
15
|
+
@extra_admin_params = {
|
16
|
+
:role => "basic",
|
17
|
+
:_api_key => "222200004444"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
shared "no new users" do
|
22
|
+
test "should not change number of user documents in database" do
|
23
|
+
assert_equal @user_count, User.all.length
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
shared "one new user" do
|
28
|
+
test "should add one user document to database" do
|
29
|
+
assert_equal @user_count + 1, User.all.length
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
shared "correct Location header" do
|
34
|
+
test "should set Location header correctly" do
|
35
|
+
base_uri = Config.environment_config["base_uri"]
|
36
|
+
path = %(/users/#{parsed_response_body["id"]})
|
37
|
+
expected = URI.join(base_uri, path).to_s
|
38
|
+
assert_equal expected, last_response.headers['Location']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "post /" do
|
43
|
+
context "anonymous" do
|
44
|
+
before do
|
45
|
+
post "/", @valid_params
|
46
|
+
end
|
47
|
+
|
48
|
+
use "return 401 because the API key is missing"
|
49
|
+
use "no new users"
|
50
|
+
end
|
51
|
+
|
52
|
+
context "incorrect API key" do
|
53
|
+
before do
|
54
|
+
post "/", @valid_params.merge(:api_key => BAD_API_KEY)
|
55
|
+
end
|
56
|
+
|
57
|
+
use "return 401 because the API key is invalid"
|
58
|
+
use "no new users"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
%w(basic curator).each do |role|
|
63
|
+
[:name, :role].each do |missing|
|
64
|
+
context "#{role} : post / but missing #{missing}" do
|
65
|
+
before do
|
66
|
+
post "/", valid_params_for(role).delete_if { |k, v| k == missing }
|
67
|
+
end
|
68
|
+
|
69
|
+
use "return 401 Unauthorized"
|
70
|
+
use "no new users"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
[:role, :_api_key, :id, :created_at, :updated_at].each do |invalid|
|
75
|
+
context "#{role} : post / but with #{invalid}" do
|
76
|
+
before do
|
77
|
+
post "/", valid_params_for(role).merge(invalid => 9)
|
78
|
+
end
|
79
|
+
|
80
|
+
use "return 401 Unauthorized"
|
81
|
+
use "no new users"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "#{role} : post / with valid params" do
|
86
|
+
before do
|
87
|
+
post "/", valid_params_for(role)
|
88
|
+
end
|
89
|
+
|
90
|
+
use "return 401 Unauthorized"
|
91
|
+
use "no new users"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
%w(admin).each do |role|
|
96
|
+
[:name, :role].each do |missing|
|
97
|
+
context "#{role} : post / but missing #{missing}" do
|
98
|
+
before do
|
99
|
+
post "/", valid_params_for(role).
|
100
|
+
merge(@extra_admin_params).delete_if { |k, v| k == missing }
|
101
|
+
end
|
102
|
+
|
103
|
+
use "return 400 Bad Request"
|
104
|
+
use "no new users"
|
105
|
+
missing_param missing
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
[:id, :created_at, :updated_at].each do |invalid|
|
110
|
+
context "#{role} : post / but with #{invalid}" do
|
111
|
+
before do
|
112
|
+
post "/", valid_params_for(role).
|
113
|
+
merge(@extra_admin_params).merge(invalid => 9)
|
114
|
+
end
|
115
|
+
|
116
|
+
use "return 400 Bad Request"
|
117
|
+
use "no new users"
|
118
|
+
invalid_param invalid
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "#{role} : post / with valid params" do
|
123
|
+
before do
|
124
|
+
post "/", valid_params_for(role).merge(@extra_admin_params)
|
125
|
+
end
|
126
|
+
|
127
|
+
use "return 201 Created"
|
128
|
+
use "correct Location header"
|
129
|
+
use "one new user"
|
130
|
+
doc_properties %w(name email role _api_key id created_at updated_at)
|
131
|
+
|
132
|
+
test "should set all fields in database" do
|
133
|
+
user = User.find_by_id(parsed_response_body["id"])
|
134
|
+
raise "Cannot find user" unless user
|
135
|
+
@valid_params.merge(@extra_admin_params).each_pair do |key, value|
|
136
|
+
assert_equal value, user[key]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
|
2
|
+
|
3
|
+
class UsersPutResourceTest < ResourceTestCase
|
4
|
+
|
5
|
+
include DataCatalog
|
6
|
+
|
7
|
+
def app; Users end
|
8
|
+
|
9
|
+
before do
|
10
|
+
@user = create_user
|
11
|
+
@user_copy = @user.dup
|
12
|
+
@valid_params = {
|
13
|
+
:name => "Changed User",
|
14
|
+
:role => "curator"
|
15
|
+
}
|
16
|
+
@extra_admin_params = {
|
17
|
+
:_api_key => "222200004444"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
@user.destroy
|
23
|
+
end
|
24
|
+
|
25
|
+
shared "user unchanged" do
|
26
|
+
test "should not change user in database" do
|
27
|
+
assert_equal @user_copy, User.find_by_id(@user.id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "put /:id" do
|
32
|
+
context "anonymous" do
|
33
|
+
before do
|
34
|
+
put "/#{@user.id}", @valid_params
|
35
|
+
end
|
36
|
+
|
37
|
+
use "return 401 because the API key is missing"
|
38
|
+
use "user unchanged"
|
39
|
+
end
|
40
|
+
|
41
|
+
context "incorrect API key" do
|
42
|
+
before do
|
43
|
+
put "/#{@user.id}", @valid_params.merge(:api_key => BAD_API_KEY)
|
44
|
+
end
|
45
|
+
|
46
|
+
use "return 401 because the API key is invalid"
|
47
|
+
use "user unchanged"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
%w(basic curator).each do |role|
|
52
|
+
[:created_at, :updated_at].each do |invalid|
|
53
|
+
context "#{role} : put / but with #{invalid}" do
|
54
|
+
before do
|
55
|
+
put "/#{@user.id}", valid_params_for(role).
|
56
|
+
merge(@extra_admin_params).merge(invalid => 9)
|
57
|
+
end
|
58
|
+
|
59
|
+
use "return 401 Unauthorized"
|
60
|
+
use "user unchanged"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
[:name, :role].each do |erase|
|
65
|
+
context "#{role} : put / but blanking out #{erase}" do
|
66
|
+
before do
|
67
|
+
put "/#{@user.id}", valid_params_for(role).
|
68
|
+
merge(@extra_admin_params).merge(erase => "")
|
69
|
+
end
|
70
|
+
|
71
|
+
use "return 401 Unauthorized"
|
72
|
+
use "user unchanged"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
[:name, :role].each do |missing|
|
77
|
+
context "#{role} : put /:id without #{missing}" do
|
78
|
+
before do
|
79
|
+
put "/#{@user.id}", valid_params_for(role).
|
80
|
+
merge(@extra_admin_params).delete_if { |k, v| k == missing }
|
81
|
+
end
|
82
|
+
|
83
|
+
use "return 401 Unauthorized"
|
84
|
+
use "user unchanged"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "#{role} : put /:id with valid params" do
|
89
|
+
before do
|
90
|
+
put "/#{@user.id}", valid_params_for(role).merge(@extra_admin_params)
|
91
|
+
end
|
92
|
+
|
93
|
+
use "return 401 Unauthorized"
|
94
|
+
use "user unchanged"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
%w(admin).each do |role|
|
99
|
+
[:created_at, :updated_at].each do |invalid|
|
100
|
+
context "#{role} : put / but with #{invalid}" do
|
101
|
+
before do
|
102
|
+
put "/#{@user.id}", valid_params_for(role).
|
103
|
+
merge(@extra_admin_params).merge(invalid => 9)
|
104
|
+
end
|
105
|
+
|
106
|
+
use "return 400 Bad Request"
|
107
|
+
use "user unchanged"
|
108
|
+
invalid_param invalid
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
[:name, :role].each do |erase|
|
113
|
+
context "#{role} : put / but blanking out #{erase}" do
|
114
|
+
before do
|
115
|
+
put "/#{@user.id}", valid_params_for(role).
|
116
|
+
merge(@extra_admin_params).merge(erase => "")
|
117
|
+
end
|
118
|
+
|
119
|
+
use "return 400 Bad Request"
|
120
|
+
use "user unchanged"
|
121
|
+
missing_param erase
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "#{role} : put /:id with no parameters" do
|
126
|
+
before do
|
127
|
+
put "/#{@user.id}", :api_key => api_key_for(role)
|
128
|
+
end
|
129
|
+
|
130
|
+
use "return 400 because no parameters were given"
|
131
|
+
use "user unchanged"
|
132
|
+
end
|
133
|
+
|
134
|
+
[:name, :role].each do |missing|
|
135
|
+
context "#{role} : put /:id without #{missing}" do
|
136
|
+
before do
|
137
|
+
put "/#{@user.id}", valid_params_for(role).
|
138
|
+
merge(@extra_admin_params).delete_if { |k, v| k == missing }
|
139
|
+
end
|
140
|
+
|
141
|
+
use "return 200 Ok"
|
142
|
+
doc_properties %w(name email role _api_key id created_at updated_at)
|
143
|
+
|
144
|
+
test "should change correct fields in database" do
|
145
|
+
user = User.find_by_id(@user.id)
|
146
|
+
@valid_params.merge(@extra_admin_params).each_pair do |key, value|
|
147
|
+
assert_equal(value, user[key]) if key != missing
|
148
|
+
end
|
149
|
+
assert_equal @user_copy[missing], user[missing]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "#{role} : put /:id with valid params" do
|
155
|
+
before do
|
156
|
+
put "/#{@user.id}", valid_params_for(role).merge(@extra_admin_params)
|
157
|
+
end
|
158
|
+
|
159
|
+
use "return 200 Ok"
|
160
|
+
doc_properties %w(name email role _api_key id created_at updated_at)
|
161
|
+
|
162
|
+
test "should change all fields in database" do
|
163
|
+
user = User.find_by_id(@user.id)
|
164
|
+
@valid_params.merge(@extra_admin_params).each_pair do |key, value|
|
165
|
+
assert_equal value, user[key]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
@@ -0,0 +1,319 @@
|
|
1
|
+
module SinatraResource
|
2
|
+
|
3
|
+
class Builder
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
|
7
|
+
# Build a resource, based on +document+, appropriate for +role+.
|
8
|
+
#
|
9
|
+
# @param [Symbol] role
|
10
|
+
# a role (such as :anonymous, :basic, or :admin)
|
11
|
+
#
|
12
|
+
# @param [MongoMapper::Document] document
|
13
|
+
#
|
14
|
+
# @return [Hash<String => Object>]
|
15
|
+
def build_resource(role, document)
|
16
|
+
resource = {}
|
17
|
+
config[:properties].each_pair do |property, hash|
|
18
|
+
if authorized?(:read, role, property)
|
19
|
+
resource[property.to_s] = value(property, document, hash)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
resource
|
23
|
+
end
|
24
|
+
|
25
|
+
# Builds a list of resources, based on +documents+, using the
|
26
|
+
# appropriate role for each document. (Delegates to +lookup_role+.)
|
27
|
+
#
|
28
|
+
# @param [Array<MongoMapper::Document>] documents
|
29
|
+
#
|
30
|
+
# @param [String] api_key
|
31
|
+
#
|
32
|
+
# @return [Array<Hash<String => Object>>]
|
33
|
+
def build_resources(documents)
|
34
|
+
documents.map do |document|
|
35
|
+
build_resource(lookup_role(document), document)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Halt unless the current params are ok for +action+ and +role+.
|
40
|
+
#
|
41
|
+
# @param [Symbol] action
|
42
|
+
# :read, :create, :update, or :delete
|
43
|
+
#
|
44
|
+
# @param [Symbol] role
|
45
|
+
# a role (such as :anonymous, :basic, or :admin)
|
46
|
+
#
|
47
|
+
# @return [undefined]
|
48
|
+
def check_params(action, role)
|
49
|
+
params_check_action(action)
|
50
|
+
params_check_action_and_role(action, role)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Halt unless the current role has permission to carry out +action+
|
54
|
+
#
|
55
|
+
# @param [Symbol] action
|
56
|
+
# :read, :create, :update, or :delete
|
57
|
+
#
|
58
|
+
# @param [Symbol] role
|
59
|
+
# a role (such as :anonymous, :basic, or :admin)
|
60
|
+
#
|
61
|
+
# @return [undefined]
|
62
|
+
def check_permission(action, role)
|
63
|
+
before_authorization(action, role)
|
64
|
+
unless authorized?(action, role)
|
65
|
+
error 401, convert(body_for(:unauthorized))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert +object+ to desired format.
|
70
|
+
#
|
71
|
+
# For example, an application might want to convert +object+ to JSON or
|
72
|
+
# XML.
|
73
|
+
#
|
74
|
+
# Applications must override this method.
|
75
|
+
#
|
76
|
+
# @param [Object] object
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
def convert
|
80
|
+
raise NotImplementedError
|
81
|
+
end
|
82
|
+
|
83
|
+
# Display +object+ as appropriate for +action+.
|
84
|
+
#
|
85
|
+
# @param [Object] object
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
def display(action, object)
|
89
|
+
case action
|
90
|
+
when :read
|
91
|
+
when :create
|
92
|
+
response.status = 201
|
93
|
+
path = config[:path] + %(/#{object["id"]})
|
94
|
+
response.headers['Location'] = full_uri(path)
|
95
|
+
when :update
|
96
|
+
when :delete
|
97
|
+
response.status = 204
|
98
|
+
end
|
99
|
+
convert(object)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Convert a path to a full URI.
|
103
|
+
#
|
104
|
+
# Applications must override this method.
|
105
|
+
#
|
106
|
+
# @param [String] path
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
def full_uri(path)
|
110
|
+
raise NotImplementedError
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get role, using +id+ if specified. Delegates to +lookup_role+.
|
114
|
+
#
|
115
|
+
# When +id+ is present, it can help determine 'relative' roles such
|
116
|
+
# as 'ownership' of the current user of a particular document.
|
117
|
+
#
|
118
|
+
# @param [String, nil] id
|
119
|
+
#
|
120
|
+
# @return [Symbol]
|
121
|
+
def get_role(id=nil)
|
122
|
+
lookup_role(id ? config[:model].find_by_id(id) : nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return the minimum role required for +action+, and, if specified,
|
126
|
+
# +property+.
|
127
|
+
#
|
128
|
+
# @param [Symbol] action
|
129
|
+
# :read, :create, :update, or :delete
|
130
|
+
#
|
131
|
+
# @return [Symbol]
|
132
|
+
# a role (such as :anonymous, :basic, or :admin)
|
133
|
+
def minimum_role(action, property=nil)
|
134
|
+
if property.nil?
|
135
|
+
config[:permission][to_read_or_modify(action)]
|
136
|
+
else
|
137
|
+
config[:properties][property][to_r_or_w(action)]
|
138
|
+
end || :anonymous
|
139
|
+
end
|
140
|
+
|
141
|
+
protected
|
142
|
+
|
143
|
+
# Is +role+ authorized for +action+, and, if specified, +property+?
|
144
|
+
#
|
145
|
+
# @param [Symbol] role
|
146
|
+
# a role (such as :anonymous, :basic, or :admin)
|
147
|
+
#
|
148
|
+
# @param [Symbol] action
|
149
|
+
# :read, :create, :update, or :delete
|
150
|
+
#
|
151
|
+
# @param [Symbol] property
|
152
|
+
# a property of a resource
|
153
|
+
#
|
154
|
+
# @return [Boolean]
|
155
|
+
def authorized?(action, role, property=nil)
|
156
|
+
klass = config[:roles]
|
157
|
+
klass.validate_role(role)
|
158
|
+
klass.satisfies?(role, minimum_role(action, property))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Application-level hook that runs as part of +check_permission+,
|
162
|
+
# before +authorized?(action, role)+ is called.
|
163
|
+
#
|
164
|
+
# For example, an application might want to throw custom errors
|
165
|
+
# in certain situations before +authorized?+ runs.
|
166
|
+
#
|
167
|
+
# Applications must override this method.
|
168
|
+
#
|
169
|
+
# @param [Symbol] action
|
170
|
+
# :read, :create, :update, or :delete
|
171
|
+
#
|
172
|
+
# @param [Symbol] role
|
173
|
+
# a role (such as :anonymous, :basic, or :admin)
|
174
|
+
#
|
175
|
+
# @return [String]
|
176
|
+
def before_authorization(action, role)
|
177
|
+
raise NotImplementedError
|
178
|
+
end
|
179
|
+
|
180
|
+
# Default body message for a +situation+
|
181
|
+
#
|
182
|
+
# @param [Symbol] situation
|
183
|
+
#
|
184
|
+
# @param [Object] object
|
185
|
+
#
|
186
|
+
# @return [String]
|
187
|
+
def body_for(situation, object=nil)
|
188
|
+
case situation
|
189
|
+
when :errors
|
190
|
+
{ "errors" => object }
|
191
|
+
when :internal_server_error
|
192
|
+
""
|
193
|
+
when :invalid_document
|
194
|
+
{ "errors" => object.errors.errors }
|
195
|
+
when :invalid_params
|
196
|
+
{ "errors" => { "invalid_params" => object } }
|
197
|
+
when :no_params
|
198
|
+
{ "errors" => "no_params" }
|
199
|
+
when :non_empty_params
|
200
|
+
{ "errors" => "non_empty_params" }
|
201
|
+
when :not_found
|
202
|
+
""
|
203
|
+
when :unauthorized
|
204
|
+
""
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Lookup the rol, using +document+ if specified.
|
209
|
+
#
|
210
|
+
# Applications must override this method.
|
211
|
+
#
|
212
|
+
# @param [MongoMapper::Document, nil] document
|
213
|
+
#
|
214
|
+
# @return [Symbol]
|
215
|
+
def lookup_role(document=nil)
|
216
|
+
raise NotImplementedError
|
217
|
+
end
|
218
|
+
|
219
|
+
# Are the params suitable for +action+? Raise 400 Bad Request if not.
|
220
|
+
#
|
221
|
+
# @param [Symbol] action
|
222
|
+
# :read, :create, :update, or :delete
|
223
|
+
#
|
224
|
+
# @return [undefined]
|
225
|
+
def params_check_action(action)
|
226
|
+
case action
|
227
|
+
when :read
|
228
|
+
unless params.empty?
|
229
|
+
error 400, convert(body_for(:non_empty_params))
|
230
|
+
end
|
231
|
+
when :create
|
232
|
+
# No need to complain. If there are problems,
|
233
|
+
# params_check_action_and_role will catch them.
|
234
|
+
when :update
|
235
|
+
if params.empty?
|
236
|
+
error 400, convert(body_for(:no_params))
|
237
|
+
end
|
238
|
+
when :delete
|
239
|
+
unless params.empty?
|
240
|
+
error 400, convert(body_for(:non_empty_params))
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Checks each parameter to make sure it is authorized for +action+ and
|
246
|
+
# +role+. Raises a 400 Bad Request if not authorized.
|
247
|
+
#
|
248
|
+
# @param [Symbol] action
|
249
|
+
# :read, :create, :update, or :delete
|
250
|
+
#
|
251
|
+
# @param [Symbol] role
|
252
|
+
# a role (such as :anonymous, :basic, or :admin)
|
253
|
+
#
|
254
|
+
# @return [undefined]
|
255
|
+
def params_check_action_and_role(action, role)
|
256
|
+
invalid = []
|
257
|
+
params.each_pair do |property, value|
|
258
|
+
invalid << property if !authorized?(action, role, property.intern)
|
259
|
+
end
|
260
|
+
unless invalid.empty?
|
261
|
+
error 400, convert(body_for(:invalid_params, invalid))
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Converts +action+ to :r or :w (i.e. read or write).
|
266
|
+
#
|
267
|
+
# @param [Symbol] action
|
268
|
+
# :read, :create, or :update
|
269
|
+
#
|
270
|
+
# @return [Symbol]
|
271
|
+
# :r or :w
|
272
|
+
def to_r_or_w(action)
|
273
|
+
case action
|
274
|
+
when :read then :r
|
275
|
+
when :create then :w
|
276
|
+
when :update then :w
|
277
|
+
else raise "Unexpected action : #{action.inspect}"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Converts +action+ to (:read or :modify).
|
282
|
+
#
|
283
|
+
# @param [Symbol] action
|
284
|
+
# :read, :create, :update, or :delete
|
285
|
+
#
|
286
|
+
# @return [Symbol]
|
287
|
+
# :read or :modify
|
288
|
+
def to_read_or_modify(action)
|
289
|
+
case action
|
290
|
+
when :read then :read
|
291
|
+
when :create then :modify
|
292
|
+
when :update then :modify
|
293
|
+
when :delete then :modify
|
294
|
+
else raise "Unexpected action : #{action.inspect}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Lookup +attribute+ in +document+
|
299
|
+
#
|
300
|
+
# @param [Symbol] attribute
|
301
|
+
# an attribute of +document+
|
302
|
+
#
|
303
|
+
# @param [MongoMapper::Document] document
|
304
|
+
#
|
305
|
+
# @return [undefined]
|
306
|
+
def value(attribute, document, property_hash)
|
307
|
+
proc = property_hash[:read_proc]
|
308
|
+
if proc
|
309
|
+
proc.call(document)
|
310
|
+
else
|
311
|
+
document[attribute == :id ? :_id : attribute]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|