trust 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/MIT-LICENSE +23 -0
  2. data/README.md +244 -0
  3. data/Rakefile +37 -0
  4. data/lib/tasks/trust_tasks.rake +42 -0
  5. data/lib/trust/active_record.rb +65 -0
  6. data/lib/trust/authorization.rb +85 -0
  7. data/lib/trust/controller/properties.rb +134 -0
  8. data/lib/trust/controller/resource.rb +306 -0
  9. data/lib/trust/controller.rb +197 -0
  10. data/lib/trust/exceptions.rb +45 -0
  11. data/lib/trust/inheritable_attribute.rb +91 -0
  12. data/lib/trust/permissions.rb +268 -0
  13. data/lib/trust/test_helper.rb +56 -0
  14. data/lib/trust/version.rb +27 -0
  15. data/lib/trust.rb +39 -0
  16. data/test/dummy/README.rdoc +261 -0
  17. data/test/dummy/Rakefile +7 -0
  18. data/test/dummy/app/assets/javascripts/accounts.js +2 -0
  19. data/test/dummy/app/assets/javascripts/application.js +15 -0
  20. data/test/dummy/app/assets/javascripts/clients.js +2 -0
  21. data/test/dummy/app/assets/javascripts/users.js +2 -0
  22. data/test/dummy/app/assets/stylesheets/accounts.css +4 -0
  23. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  24. data/test/dummy/app/assets/stylesheets/clients.css +4 -0
  25. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  26. data/test/dummy/app/assets/stylesheets/users.css +4 -0
  27. data/test/dummy/app/controllers/accounts_controller.rb +100 -0
  28. data/test/dummy/app/controllers/application_controller.rb +31 -0
  29. data/test/dummy/app/controllers/clients_controller.rb +107 -0
  30. data/test/dummy/app/controllers/savings_accounts_controller.rb +27 -0
  31. data/test/dummy/app/controllers/settlements_controller.rb +26 -0
  32. data/test/dummy/app/controllers/users_controller.rb +107 -0
  33. data/test/dummy/app/helpers/accounts_helper.rb +26 -0
  34. data/test/dummy/app/helpers/application_helper.rb +26 -0
  35. data/test/dummy/app/helpers/clients_helper.rb +26 -0
  36. data/test/dummy/app/helpers/users_helper.rb +26 -0
  37. data/test/dummy/app/models/account/credit.rb +26 -0
  38. data/test/dummy/app/models/account.rb +35 -0
  39. data/test/dummy/app/models/client.rb +35 -0
  40. data/test/dummy/app/models/permissions.rb +68 -0
  41. data/test/dummy/app/models/savings_account.rb +26 -0
  42. data/test/dummy/app/models/user.rb +40 -0
  43. data/test/dummy/app/views/accounts/_form.html.erb +46 -0
  44. data/test/dummy/app/views/accounts/edit.html.erb +31 -0
  45. data/test/dummy/app/views/accounts/index.html.erb +48 -0
  46. data/test/dummy/app/views/accounts/new.html.erb +30 -0
  47. data/test/dummy/app/views/accounts/show.html.erb +35 -0
  48. data/test/dummy/app/views/clients/_form.html.erb +46 -0
  49. data/test/dummy/app/views/clients/edit.html.erb +31 -0
  50. data/test/dummy/app/views/clients/index.html.erb +48 -0
  51. data/test/dummy/app/views/clients/new.html.erb +30 -0
  52. data/test/dummy/app/views/clients/show.html.erb +35 -0
  53. data/test/dummy/app/views/layouts/application.html.erb +39 -0
  54. data/test/dummy/app/views/users/_form.html.erb +46 -0
  55. data/test/dummy/app/views/users/edit.html.erb +31 -0
  56. data/test/dummy/app/views/users/index.html.erb +48 -0
  57. data/test/dummy/app/views/users/new.html.erb +30 -0
  58. data/test/dummy/app/views/users/show.html.erb +35 -0
  59. data/test/dummy/config/application.rb +56 -0
  60. data/test/dummy/config/boot.rb +10 -0
  61. data/test/dummy/config/database.yml +25 -0
  62. data/test/dummy/config/environment.rb +5 -0
  63. data/test/dummy/config/environments/development.rb +37 -0
  64. data/test/dummy/config/environments/production.rb +67 -0
  65. data/test/dummy/config/environments/test.rb +37 -0
  66. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  67. data/test/dummy/config/initializers/inflections.rb +15 -0
  68. data/test/dummy/config/initializers/mime_types.rb +5 -0
  69. data/test/dummy/config/initializers/secret_token.rb +7 -0
  70. data/test/dummy/config/initializers/session_store.rb +8 -0
  71. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  72. data/test/dummy/config/locales/en.yml +5 -0
  73. data/test/dummy/config/routes.rb +38 -0
  74. data/test/dummy/config.ru +4 -0
  75. data/test/dummy/db/migrate/20120522115011_create_accounts.rb +36 -0
  76. data/test/dummy/db/migrate/20120522130322_create_users.rb +33 -0
  77. data/test/dummy/db/migrate/20120523144144_create_clients.rb +34 -0
  78. data/test/dummy/db/schema.rb +38 -0
  79. data/test/dummy/public/404.html +26 -0
  80. data/test/dummy/public/422.html +26 -0
  81. data/test/dummy/public/500.html +25 -0
  82. data/test/dummy/public/favicon.ico +0 -0
  83. data/test/dummy/script/rails +6 -0
  84. data/test/dummy/test/fixtures/accounts.yml +7 -0
  85. data/test/dummy/test/fixtures/clients.yml +7 -0
  86. data/test/dummy/test/fixtures/users.yml +7 -0
  87. data/test/dummy/test/functional/accounts_controller_test.rb +123 -0
  88. data/test/dummy/test/functional/clients_controller_test.rb +74 -0
  89. data/test/dummy/test/functional/users_controller_test.rb +74 -0
  90. data/test/dummy/test/unit/account_test.rb +31 -0
  91. data/test/dummy/test/unit/client_test.rb +31 -0
  92. data/test/dummy/test/unit/helpers/accounts_helper_test.rb +28 -0
  93. data/test/dummy/test/unit/helpers/clients_helper_test.rb +28 -0
  94. data/test/dummy/test/unit/helpers/users_helper_test.rb +28 -0
  95. data/test/dummy/test/unit/permissions_test.rb +171 -0
  96. data/test/dummy/test/unit/user_test.rb +31 -0
  97. data/test/test_helper.rb +45 -0
  98. data/test/trust_test.rb +31 -0
  99. data/test/unit/trust/active_record_test.rb +56 -0
  100. data/test/unit/trust/authorization_test.rb +108 -0
  101. data/test/unit/trust/controller/properties_test.rb +132 -0
  102. data/test/unit/trust/controller/resource_test.rb +251 -0
  103. data/test/unit/trust/controller_test.rb +160 -0
  104. data/test/unit/trust/inheritable_attribute_test.rb +65 -0
  105. data/test/unit/trust/permissions_test.rb +258 -0
  106. metadata +280 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright 2012 Bingo Entreprenøren AS
2
+ Copyright 2012 Teknobingo Scandinavia AS
3
+ Copyright 2012 Knut I. Stenmark
4
+ Copyright 2012 Patrick Hanevold
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # Trust
2
+
3
+ ### Trust is a no-nonsense framework for authorization control - for Ruby On Rails.
4
+
5
+ - Why yet another authorization framework you may ask?
6
+
7
+ Well, we used [DeclarativeAuthorization](http://github.com/stffn/declarative_authorization) for a while, but got stuck when it comes to name-spaces and inheritance. So, we investigated in the possibilities of using [CanCan](http://github.com/ryanb/cancan) and [CanTango](http://github.com/kristianmandrup/cantango), and found that CanCan could be slow, because all permissions has to be loaded on every request. CanTango has tackled this problem by implementing caching, but the framework is still evolving and seems fairly complex. At the same time, CanTango is role focused and not resource focused.
8
+
9
+ ### What will you benefit from when using Trust?
10
+
11
+ * Resource focused permissions, not role focused
12
+ * Complete support for inheritance in controllers
13
+ * Complete support for namespaces, both controllers and models
14
+ * Complete support for nested resources
15
+ * Complete support for shortened associations (e.g. if you have models in name spaces that relates to other models in the name space)
16
+ * Fast permission loading, where no cashing is needed. All permissions are declared on class level, so once loaded, they stay in memory.
17
+ * Support for inheritance in the permissions model
18
+ * Natural code evaluation in the permission declarations, i.e. you understand completely what is going on, because the implementation is done the way you implement condifitions in rails for validations and alike.
19
+ * Automatic loading of instances and parents in controller
20
+
21
+ ### What is not supported in Trust
22
+
23
+ * Loading datasets for the index action. You may use other plugins / gems for doing this, or you may implement your own mechanism.
24
+
25
+ ### Currently not supported, but may be in the future
26
+
27
+ * Support for devise. However you may easily implement this by overriding one method in your controller.
28
+ * cannot and cannot? expressions.
29
+
30
+ # Install and Setup
31
+
32
+ Install the gem
33
+
34
+ gem install trust
35
+
36
+ ### Define permissions
37
+
38
+ Create the permissions file in your model directory. Example
39
+
40
+ ``` Ruby
41
+ module Permissions
42
+ class Default < Trust::Permissions
43
+ role :system_admin do
44
+ can :manage
45
+ can :audit
46
+ end
47
+ end
48
+
49
+ class Account < Default
50
+ role :support, can(:manage)
51
+ role :accountant do
52
+ can :edit, :show, :if => :associated_with_client?
53
+ end
54
+ role :department_manager, :accountant do
55
+ can :create, :if => lambda { parent }
56
+ end
57
+
58
+ def associated_with_client?
59
+ parent && parent.is_a?(Client) && parent.operators.find(user.id)
60
+ end
61
+ end
62
+ end
63
+ ```
64
+
65
+ The following attributes will be accessible in a Permissions class:
66
+
67
+ * ```subject``` - the resource that is currently being tested for authorization
68
+ * ```parent``` - the parent of the authorization when resource is nested
69
+ * ```user``` - the user accessing the resource
70
+ * ```klass``` - the resource class
71
+ * ```action``` - the action that triggered the authorization
72
+
73
+ Keep in mind that the permission object will be instanciated to do authorization, and not the class.
74
+ You can extend the Trust::Permissions with more functionality if needed.
75
+
76
+ You can also create aliases for actions. We have defined a predefined set of aliases. See Trust::Permissions.action_aliases.
77
+ Processing of aliases are done in such way that permissions per action is expanded when the permissions are loaded, so thif you define :update when declaring the permissions, there will be one permission for :update and one for :edit
78
+
79
+
80
+ ### Apply access control in controller
81
+
82
+ Place ```trustee``` in your controller after the user has been identified. Something like this:
83
+
84
+ ``` Ruby
85
+ class AccountsController < ApplicationController
86
+ login_required
87
+ trustee
88
+ end
89
+ ```
90
+
91
+ The trustee statement will set up 3 before_filters in your controller:
92
+
93
+ ``` Ruby
94
+ before_filter :set_user
95
+ before_filter :load_resource
96
+ before_filter :access_control
97
+ ```
98
+
99
+ Trust assumes that ```current_user``` is accessible. The user object must respond to the method ```role_symbols``` which should return an array of one or more roles for the user.
100
+
101
+ Handling access denied situations in your controller. Implement something like the following in your ApplicationController:
102
+
103
+ ``` Ruby
104
+ class ApplicationController < ActionController::Base
105
+ rescue_from Trust::AccessDenied do |exception|
106
+ redirect_to root_url, :alert => exception.message
107
+ # or some other redirect
108
+ end
109
+ end
110
+ ```
111
+
112
+ ### Define associations in your controller
113
+
114
+ For nested resources you can easily define the associations using ```belongs_to``` like this:
115
+
116
+ ``` Ruby
117
+ class AccountsController < ApplicationController
118
+ login_required
119
+ belongs_to :client
120
+ trustee
121
+ end
122
+ ```
123
+
124
+ You can specify as many associations as you like.
125
+
126
+
127
+ ## The can? and permits? method
128
+
129
+ The can? method is accessible from controller and views. Here are some coding examples:
130
+
131
+ #### In controller or views you will use can?
132
+
133
+ ``` Ruby
134
+ can? :edit # does the current user have permission to edit the current resource?
135
+ # If there is a nested resource, the parent is automatically associated
136
+ can? :edit, @customer # does the current user have permission to edit the given customer?
137
+ # Parent is also passed on here.
138
+ can? :edit, @account, @client # is current user allowed to edit the account associated with the client?
139
+ ```
140
+
141
+ #### On ActiveRecord objects you will use permits?
142
+
143
+ ``` Ruby
144
+ @customer.permits? :edit # does the current user have permission to edit the given customer?
145
+ Customer.permits? :create, @client # does the current user have permission to create customers?
146
+ ```
147
+
148
+ ## Instance variables
149
+
150
+ The filter ```load_resource``` will automatically load the instance for the resource in the controller. It will by default use the controller_path to determine the name of the instance variable. Here are a couple of examples:
151
+
152
+ ``` Ruby
153
+ UsersController => @user
154
+ Account::CreditsController => @account_credit
155
+ ```
156
+
157
+ If it is a nested resource, it will also instantiate the ```parent``` class, using the namedefined in belongs_to to determine the name. E.g. if you have defined belongs_to :client, it will look for the parameter ```:client_id``` and perform a find like ```Client.find(client_id)```. Finding the resource will be done through the association between the two, such as ```client.accounts.find(id)```.
158
+
159
+ You can override the naming by specifying ```model``` like this
160
+
161
+ ``` Ruby
162
+ class AccountsController < ApplicationController
163
+ login_required
164
+ model :wackount
165
+ trustee
166
+ end
167
+ ```
168
+
169
+ If you want to override the name with namespacing then
170
+
171
+ ``` Ruby
172
+ class Account::CreditsController < ApplicationController
173
+ login_required
174
+ model :"account/wreckit"
175
+ trustee
176
+ end
177
+ ```
178
+
179
+ You can also access the instances in a generic manner if you like. Use following statements:
180
+
181
+ ``` Ruby
182
+ resource.instance => accesses the instance variable
183
+ resource.parent => accesses the parent instance
184
+ ```
185
+
186
+ You can even assign these if you like. The resource is also exposed as helper, so you can access it in views.
187
+ For simplicity we have also exposed an ```instances``` accessor that you can assign when you have a multirecord result,
188
+ such as for index action.
189
+
190
+ ## Overriding defaults
191
+
192
+ ### Overriding resource permits in the controller
193
+
194
+ Say you have a controller without a model or do not want to perform access control. You can turn off the featur in your controller
195
+
196
+ ``` Ruby
197
+ class ApplicationController < ActionController::Base
198
+ login_required
199
+ trustee # By default we want to test for permissions
200
+ end
201
+
202
+ class MyController < ApplicationController
203
+ trustee :off # turns off all callbacks
204
+ end
205
+ ```
206
+
207
+ #### Alternatives
208
+ ``` Ruby
209
+ class MyController < ApplicationController
210
+ set_user :off # turns off set_user callback
211
+ load_resource :off # do not load resources
212
+ access_control :off # turn access control off
213
+ end
214
+ ```
215
+
216
+ #### More specifically
217
+ For all call backs and ```trustee``` you can use ```:only``` and ```:except``` options.
218
+ Example toggle create action off
219
+ ``` Ruby
220
+ class MyController < ApplicationController
221
+ load_resource :except => :create
222
+ access_control :except => :create
223
+ end
224
+ ```
225
+
226
+ #### Yet another alternative, avoiding resource loading
227
+ Avoid resource loading on ```show``` action
228
+ ``` Ruby
229
+ class MyController < ApplicationController
230
+ actions :except => :show
231
+ end
232
+ ```
233
+
234
+ ### Overriding set_user
235
+
236
+ If you prefer to use some other user reference than current_user you can override the method ```set_user``` like this in your controller:
237
+
238
+ ``` Ruby
239
+ def set_user
240
+ Trust::Authorization.user = User.current
241
+ end
242
+ ```
243
+
244
+
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Trust'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Dir.glob('lib/tasks/*.rake').each { |r| import r }
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task :default => :test
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ namespace :db do
26
+ DELEGATORS = [ :create, :drop, :migrate, :rollback, :setup ]
27
+ DESCRIPTORS = Hash[*`cd test/dummy; rake -T`.split("\n").select{ |line| line =~ /^rake db:/ }.map{ |line| line.split('#').map{ |str| str.strip } }.flatten]
28
+
29
+ DELEGATORS.each do |delegate|
30
+ desc DESCRIPTORS["rake db:#{delegate}"]
31
+ task delegate do
32
+ system "cd test/dummy; rake db:#{delegate}"
33
+ end
34
+ end
35
+
36
+ namespace :test do
37
+ desc 'preparte test database with schema'
38
+ task :prepare do
39
+ system "cd test/dummy; rake db:test:prepare"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module Trust
26
+ module ActiveRecord
27
+ extend ActiveSupport::Concern
28
+ # = Trust::ActiveRecord extension
29
+ # Extends ActiveRecord with the +permits?+ and +ensure_permitted!+ method on class and instances
30
+ #
31
+ # ==== Examples
32
+ #
33
+ # # If current user is permitted to create customers, create it
34
+ # if Customer.permits? :create
35
+ # Customer.create attributes
36
+ # end
37
+ #
38
+ # # If current user is permitted to create accounts for the given customer, create it
39
+ # if Account.permits? :create, @customer
40
+ # Account.create attributes
41
+ # end
42
+ #
43
+ # # If current user is permitted to update the given customer, update it
44
+ # if @customer.permits? :update
45
+ # @customer.save
46
+ # end
47
+ #
48
+ # # Raise an exception if user is not permitted to update the given customer, else save it
49
+ # @customer.ensure_permitted! :update
50
+ # @customer.save
51
+
52
+ included do
53
+ include ClassMethods
54
+ end
55
+
56
+ module ClassMethods
57
+ def permits?(action, parent = nil)
58
+ Trust::Authorization.authorized?(action, self, parent)
59
+ end
60
+ def ensure_permitted!(action, parent = nil)
61
+ Trust::Authorization.authorize!(action, self, parent)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,85 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module Trust
26
+ class Authorization
27
+ class << self
28
+ # Returns true if user is authorized to perform +action+ on +object+ or +class+
29
+ # If +parent+ is given, +parent+ may be tested in the implemented Permissions class
30
+ # This method is called by the +can?+ method in Trust::Controller, and is normally
31
+ # not necessary to call directly.
32
+ def authorized?(action, object_or_class, parent)
33
+ if object_or_class.is_a? Class
34
+ klass = object_or_class
35
+ object = nil
36
+ else
37
+ klass = object_or_class.class
38
+ object = object_or_class
39
+ end
40
+ # Identify which class to instanciate and then check authorization
41
+ auth = authorizing_class(klass)
42
+ # Rails.logger.debug "Trust: Authorizing class for #{klass.name} is #{auth.name}"
43
+ auth.new(user, action.to_sym, klass, object, parent).authorized?
44
+ end
45
+
46
+ # Tests if user is authorized to perform +action+ on +object+ or +class+, with the
47
+ # optional parent and raises Trust::AccessDenied exception if not permitted.
48
+ # If using this method directly, an optional +message+ can be passed in to
49
+ # replace the default message used.
50
+ # This method is used by the +access_control+ method in Trust::Controller
51
+ def authorize!(action, object_or_class, parent, message = nil)
52
+ access_denied!(message, action, object_or_class, parent) unless authorized?(action, object_or_class, parent)
53
+ end
54
+
55
+ def access_denied!(message = nil, action = nil, subject = nil, parent = nil) # nodoc
56
+ raise AccessDenied.new(message, action, subject)
57
+ end
58
+
59
+ # returns the current +user+ being used in the authorization process
60
+ def user
61
+ Thread.current["current_user"]
62
+ end
63
+
64
+ # sets the current +user+ to be used in the authorization process.
65
+ # the +user+ is thread safe.
66
+ def user=(user)
67
+ Thread.current["current_user"] = user
68
+ end
69
+
70
+ private
71
+ def authorizing_class(klass) # nodoc
72
+ auth = nil
73
+ klass.ancestors.each do |k|
74
+ break if k == ::ActiveRecord::Base
75
+ begin
76
+ auth = "::Permissions::#{k}".constantize
77
+ break
78
+ rescue
79
+ end
80
+ end
81
+ auth || ::Permissions::Default
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,134 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module Trust
26
+ module Controller
27
+ class Properties
28
+ delegate :logger, :to => Rails
29
+ attr_reader :controller
30
+ attr_accessor :model
31
+ attr_accessor :associations
32
+ attr_accessor :new_actions
33
+ attr_accessor :member_actions
34
+ attr_accessor :collection_actions
35
+
36
+ def initialize(controller, properties) # nodoc
37
+ @controller = controller
38
+ @model = controller.controller_path
39
+ if properties
40
+ @associations = properties.associations.dup
41
+ @new_actions = properties.new_actions.dup
42
+ @member_actions = properties.member_actions.dup
43
+ @collection_actions = properties.collection_actions.dup
44
+ else
45
+ @associations = {}
46
+ @new_actions = [:new, :create]
47
+ @member_actions = [:show, :edit, :update, :destroy]
48
+ @collection_actions = [:index]
49
+ end
50
+ end
51
+
52
+ class << self
53
+ # returns a controller properties object
54
+ # ensures controller properties are instantiated in a correct manner and that inheritance is supported
55
+ def instantiate(controller)
56
+ new(controller, controller.superclass.instance_variable_get(:@properties))
57
+ end
58
+ end
59
+
60
+ # returns or sets the model to be used in a controller
61
+ # If not set, the controller_path is used
62
+ # You can override the model to be accessed in a controller by setting the model
63
+ #
64
+ # ==== Example
65
+ #
66
+ # # You have a controller which inherits from a generic controller and it has not the same name. Below
67
+ # model :account # will assume that the class to be Account and instance variables to be @account/@accounts
68
+ #
69
+ # # name spaced models
70
+ # model :"customer/account"
71
+ #
72
+ def model(name = nil)
73
+ @model = name if name
74
+ @model
75
+ end
76
+
77
+ # Returns the class for the model
78
+ def model_class
79
+ model.to_s.classify.constantize
80
+ end
81
+
82
+ # Specify associated resources (nested resources)
83
+ # Example:
84
+ # belongs_to :lottery
85
+ # belongs_to :table, :card_game
86
+ # belongs_to :card_game, :as => :bridge
87
+ #
88
+ def belongs_to(*resources)
89
+ raise ArgumentError, "You must specify at least one resource after belongs_to" unless resources
90
+ logger.debug "#{@model} belongs_to #{resources.inspect}"
91
+ options = resources.extract_options!
92
+ resources.each do |resource|
93
+ @associations[resource] = options[:as]
94
+ end
95
+ end
96
+
97
+ def has_associations?
98
+ @associations.size > 0
99
+ end
100
+
101
+ # actions(options)
102
+ # Options
103
+ # :new => actions # specify new actions - default id :new, :create
104
+ # :member => actions # specify member actions - default is :show, :edit, :update, :destroy
105
+ # :collection => actions # specify collection actions - default is :index
106
+ # :except => actions # removes any standard actions
107
+ # :only => actions # selects only the standard actions specifiec
108
+ # :add => {options} # to add options, eg :add => {:new => :confirm}
109
+ #
110
+ def actions(options)
111
+ if add = options[:add]
112
+ self.new_actions += Array.wrap(add[:new]) if add[:new]
113
+ self.member_actions += Array.wrap(add[:member]) if add[:member]
114
+ self.collection_actions += Array.wrap(add[:collection]) if add[:collection]
115
+ end
116
+ self.new_actions = Array.wrap(options[:new]) if options[:new]
117
+ self.member_actions = Array.wrap(options[:member]) if options[:member]
118
+ self.collection_actions = Array.wrap(options[:collection]) if options[:collection]
119
+ if options[:only]
120
+ only = Array.wrap(options[:only])
121
+ self.new_actions &= only
122
+ self.member_actions &= only
123
+ self.collection_actions &= only
124
+ end
125
+ if options[:except]
126
+ except = Array.wrap(options[:except])
127
+ self.new_actions -= except
128
+ self.member_actions -= except
129
+ self.collection_actions -= except
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end