padrino-admin 0.7.5 → 0.7.6
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/VERSION +1 -1
- data/lib/padrino-admin.rb +13 -1
- data/lib/padrino-admin/access_control.rb +362 -361
- data/lib/padrino-admin/config.rb +1 -1
- data/lib/padrino-admin/helpers/authentication.rb +1 -1
- data/lib/padrino-admin/helpers/view.rb +6 -5
- data/lib/padrino-admin/orm.rb +8 -0
- data/lib/padrino-admin/orm/activerecord.rb +1 -1
- data/lib/padrino-admin/orm/datamapper.rb +1 -1
- data/padrino-admin.gemspec +11 -11
- data/test/test_access_control.rb +2 -2
- metadata +5 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.6
|
data/lib/padrino-admin.rb
CHANGED
@@ -8,6 +8,18 @@ Dir[File.dirname(__FILE__) + '/padrino-admin/*.rb'].each {|file| require file }
|
|
8
8
|
Dir[File.dirname(__FILE__) + '/padrino-admin/{helpers,orm,middleware,utils}/*.rb'].each {|file| require file }
|
9
9
|
Dir[File.dirname(__FILE__) + '/padrino-admin/generators/{actions,admin_app,admin_page,admin_uploader}.rb'].each {|file| require file }
|
10
10
|
|
11
|
+
module Padrino
|
12
|
+
##
|
13
|
+
# Padrino::Admin is beautiful Ajax Admin, with these fatures:
|
14
|
+
#
|
15
|
+
# Orm Agnostic:: Adapters for datamapper, activerecord, mongomapper, couchdb (now only: datamapper and activerecord)
|
16
|
+
# Authentication:: Support for Account authentication, Account Permission managment
|
17
|
+
# Scaffold:: You can simply create a new "admin interface" simply providing a Model
|
18
|
+
# Ajax Uploads:: You can upload file, manage them and attach them to any model in a quick and simple way (coming soon)
|
19
|
+
#
|
20
|
+
module Admin; end
|
21
|
+
end
|
22
|
+
|
11
23
|
##
|
12
24
|
# We need to apply Padrino::Admin::Utils::Extensions
|
13
25
|
#
|
@@ -18,7 +30,7 @@ String.send(:include, Padrino::Admin::Utils::Literal)
|
|
18
30
|
# We need to add to Padrino::Application a +access_control+ class
|
19
31
|
#
|
20
32
|
Padrino::Application.send(:cattr_accessor, :access_control)
|
21
|
-
Padrino::Application.send(:access_control=, Class.new(Padrino::AccessControl::Base))
|
33
|
+
Padrino::Application.send(:access_control=, Class.new(Padrino::Admin::AccessControl::Base))
|
22
34
|
|
23
35
|
##
|
24
36
|
# If CarrierWave is defined we set the root directory
|
@@ -1,402 +1,403 @@
|
|
1
1
|
module Padrino
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
##
|
7
|
-
# This module give to a padrino application an access control functionality like:
|
8
|
-
#
|
9
|
-
# class EcommerceDemo < Padrino::Application
|
10
|
-
# enable :authentication
|
11
|
-
# set :login_page, "/login" # or your login page
|
12
|
-
# enable :store_location # if you want know what is the page that need authentication
|
13
|
-
#
|
14
|
-
# access_control.roles_for :any do
|
15
|
-
# role.require_login "/cart"
|
16
|
-
# role.require_login "/account"
|
17
|
-
# role.allow "/account/create"
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# In the EcommerceDemo, we +only+ require logins for all paths that start with "/cart" like:
|
22
|
-
#
|
23
|
-
# - "/cart/add"
|
24
|
-
# - "/cart/empty"
|
25
|
-
# - "/cart/checkout"
|
26
|
-
#
|
27
|
-
# same thing for "/account" so we require a login for:
|
28
|
-
#
|
29
|
-
# - "/account"
|
30
|
-
# - "/account/edit"
|
31
|
-
# - "/account/update"
|
32
|
-
#
|
33
|
-
# but if we call "/account/create" we don't need to be logged in our site for do that.
|
34
|
-
# In EcommerceDemo example we set +redirect_back_or_default+ so if a +unlogged+
|
35
|
-
# user try to access "/account/edit" will be redirected to "/login" when login is done will be
|
36
|
-
# redirected to "/account/edit".
|
37
|
-
#
|
38
|
-
# If we need something more complex aka roles/permissions we can do that in the same simple way
|
39
|
-
#
|
40
|
-
# class AdminDemo < Padrino::Application
|
41
|
-
# enable :authentication
|
42
|
-
# set :login_page, "/sessions/new" # or your page
|
43
|
-
#
|
44
|
-
# access_control.roles_for :any do |role|
|
45
|
-
# role.allow "/sessions"
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# access_control.roles_for :admin do |role, account|
|
49
|
-
# role.allow "/"
|
50
|
-
# role.deny "/posts"
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# access_control.roles_for :editor do |role, account|
|
54
|
-
# role.allow "/posts"
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# If a user logged with role admin can:
|
59
|
-
#
|
60
|
-
# - Access to all paths that start with "/session" like "/sessions/{new,create}"
|
61
|
-
# - Access to any page except those that start with "/posts"
|
62
|
-
#
|
63
|
-
# If a user logged with role editor can:
|
64
|
-
#
|
65
|
-
# - Access to all paths that start with "/session" like "/sessions/{new,create}"
|
66
|
-
# - Access +only+ to paths that start with "/posts" like "/post/{new,edit,destroy}"
|
67
|
-
#
|
68
|
-
# Finally we have another good fatures, the possibility in the same time we build role build also +tree+.
|
69
|
-
# Figure this scenario: in my admin every account need their own menu, so an Account with role editor have
|
70
|
-
# a menu different than an Account with role admin.
|
71
|
-
#
|
72
|
-
# So:
|
73
|
-
#
|
74
|
-
# class AdminDemo < Padrino::Application
|
75
|
-
# enable :authentication
|
76
|
-
# set :redirect_to_default, "/" # or your page
|
77
|
-
#
|
78
|
-
# access_control.roles_for :any do |role|
|
79
|
-
# role.allow "/sessions"
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# access_control.roles_for :admin do |role, current_account|
|
83
|
-
#
|
84
|
-
# role.project_module :settings do |project|
|
85
|
-
# project.menu :accounts, "/accounts" do |accounts|
|
86
|
-
# accounts.add :new, "/accounts/new" do |account|
|
87
|
-
# account.add :administrator, "/account/new/?role=administrator"
|
88
|
-
# account.add :editor, "/account/new/?role=editor"
|
89
|
-
# end
|
90
|
-
# end
|
91
|
-
# project.menu :spam_rules, "/manage_spam"
|
92
|
-
# end
|
93
|
-
#
|
94
|
-
# role.project_module :categories do |project|
|
95
|
-
# current_account.categories.each do |category|
|
96
|
-
# project.menu category.name, "/categories/#{category.id}.js"
|
97
|
-
# end
|
98
|
-
# end
|
99
|
-
# end
|
100
|
-
#
|
101
|
-
# access_control.roles_for :editor do |role, current_account|
|
102
|
-
#
|
103
|
-
# role.project_module :posts do |posts|
|
104
|
-
# post.menu :list, "/posts"
|
105
|
-
# post.menu :new, "/posts/new"
|
106
|
-
# end
|
107
|
-
# end
|
108
|
-
#
|
109
|
-
# In this example when we build our menu tree we are also defining roles so:
|
110
|
-
#
|
111
|
-
# An Admin Account have access to:
|
112
|
-
#
|
113
|
-
# - All paths that start with "/sessions"
|
114
|
-
# - All paths that start with "/accounts"
|
115
|
-
# - All paths that start with "/manage_spam"
|
116
|
-
#
|
117
|
-
# An Editor Account have access to:
|
118
|
-
#
|
119
|
-
# - All paths that start with "/posts"
|
120
|
-
#
|
121
|
-
# Remember that you always deny a specific actions or allow globally others.
|
122
|
-
#
|
123
|
-
# Remember that when you define role_for :a_role, you have also access to the Model Account.
|
124
|
-
#
|
125
|
-
module AccessControl
|
2
|
+
module Admin
|
3
|
+
class AccessControlError < StandardError #:nodoc:
|
4
|
+
end
|
126
5
|
|
127
6
|
##
|
128
|
-
#
|
7
|
+
# This module give to a padrino application an access control functionality like:
|
8
|
+
#
|
9
|
+
# class EcommerceDemo < Padrino::Application
|
10
|
+
# enable :authentication
|
11
|
+
# set :login_page, "/login" # or your login page
|
12
|
+
# enable :store_location # if you want know what is the page that need authentication
|
129
13
|
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
14
|
+
# access_control.roles_for :any do
|
15
|
+
# role.require_login "/cart"
|
16
|
+
# role.require_login "/account"
|
17
|
+
# role.allow "/account/create"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# In the EcommerceDemo, we +only+ require logins for all paths that start with "/cart" like:
|
22
|
+
#
|
23
|
+
# - "/cart/add"
|
24
|
+
# - "/cart/empty"
|
25
|
+
# - "/cart/checkout"
|
26
|
+
#
|
27
|
+
# same thing for "/account" so we require a login for:
|
28
|
+
#
|
29
|
+
# - "/account"
|
30
|
+
# - "/account/edit"
|
31
|
+
# - "/account/update"
|
32
|
+
#
|
33
|
+
# but if we call "/account/create" we don't need to be logged in our site for do that.
|
34
|
+
# In EcommerceDemo example we set +redirect_back_or_default+ so if a +unlogged+
|
35
|
+
# user try to access "/account/edit" will be redirected to "/login" when login is done will be
|
36
|
+
# redirected to "/account/edit".
|
37
|
+
#
|
38
|
+
# If we need something more complex aka roles/permissions we can do that in the same simple way
|
39
|
+
#
|
40
|
+
# class AdminDemo < Padrino::Application
|
41
|
+
# enable :authentication
|
42
|
+
# set :login_page, "/sessions/new" # or your page
|
43
|
+
#
|
44
|
+
# access_control.roles_for :any do |role|
|
45
|
+
# role.allow "/sessions"
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# access_control.roles_for :admin do |role, account|
|
49
|
+
# role.allow "/"
|
50
|
+
# role.deny "/posts"
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# access_control.roles_for :editor do |role, account|
|
54
|
+
# role.allow "/posts"
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# If a user logged with role admin can:
|
59
|
+
#
|
60
|
+
# - Access to all paths that start with "/session" like "/sessions/{new,create}"
|
61
|
+
# - Access to any page except those that start with "/posts"
|
62
|
+
#
|
63
|
+
# If a user logged with role editor can:
|
64
|
+
#
|
65
|
+
# - Access to all paths that start with "/session" like "/sessions/{new,create}"
|
66
|
+
# - Access +only+ to paths that start with "/posts" like "/post/{new,edit,destroy}"
|
67
|
+
#
|
68
|
+
# Finally we have another good fatures, the possibility in the same time we build role build also +tree+.
|
69
|
+
# Figure this scenario: in my admin every account need their own menu, so an Account with role editor have
|
70
|
+
# a menu different than an Account with role admin.
|
71
|
+
#
|
72
|
+
# So:
|
73
|
+
#
|
74
|
+
# class AdminDemo < Padrino::Application
|
75
|
+
# enable :authentication
|
76
|
+
# set :redirect_to_default, "/" # or your page
|
77
|
+
#
|
78
|
+
# access_control.roles_for :any do |role|
|
79
|
+
# role.allow "/sessions"
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# access_control.roles_for :admin do |role, current_account|
|
83
|
+
#
|
84
|
+
# role.project_module :settings do |project|
|
85
|
+
# project.menu :accounts, "/accounts" do |accounts|
|
86
|
+
# accounts.add :new, "/accounts/new" do |account|
|
87
|
+
# account.add :administrator, "/account/new/?role=administrator"
|
88
|
+
# account.add :editor, "/account/new/?role=editor"
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
# project.menu :spam_rules, "/manage_spam"
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# role.project_module :categories do |project|
|
95
|
+
# current_account.categories.each do |category|
|
96
|
+
# project.menu category.name, "/categories/#{category.id}.js"
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# access_control.roles_for :editor do |role, current_account|
|
102
|
+
#
|
103
|
+
# role.project_module :posts do |posts|
|
104
|
+
# post.menu :list, "/posts"
|
105
|
+
# post.menu :new, "/posts/new"
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# In this example when we build our menu tree we are also defining roles so:
|
110
|
+
#
|
111
|
+
# An Admin Account have access to:
|
112
|
+
#
|
113
|
+
# - All paths that start with "/sessions"
|
114
|
+
# - All paths that start with "/accounts"
|
115
|
+
# - All paths that start with "/manage_spam"
|
116
|
+
#
|
117
|
+
# An Editor Account have access to:
|
118
|
+
#
|
119
|
+
# - All paths that start with "/posts"
|
120
|
+
#
|
121
|
+
# Remember that you always deny a specific actions or allow globally others.
|
122
|
+
#
|
123
|
+
# Remember that when you define role_for :a_role, you have also access to the Model Account.
|
124
|
+
#
|
125
|
+
module AccessControl
|
126
|
+
|
127
|
+
##
|
128
|
+
# Method used by Padrino::Application when we register the extension
|
129
|
+
#
|
130
|
+
def self.registered(app)
|
131
|
+
app.set :session_id, "_padrino_#{File.basename(Padrino.root)}_#{app.app_name}".to_sym
|
132
|
+
app.helpers Padrino::Admin::Helpers
|
133
|
+
app.before { login_required }
|
134
|
+
app.use Padrino::Admin::Middleware::FlashMiddleware, app.session_id # make sure that is the same of session_name in helpers
|
135
|
+
Padrino::Admin::Orm.extend_account!
|
136
|
+
end
|
137
|
+
|
138
|
+
class Base
|
139
|
+
class << self
|
140
|
+
attr_reader :roles
|
137
141
|
|
138
|
-
|
139
|
-
|
140
|
-
|
142
|
+
def inherited(base) #:nodoc:
|
143
|
+
base.class_eval("@@cache={}; @authorizations=[]; @roles=[]; @mappers=[]")
|
144
|
+
base.send(:cattr_reader, :cache)
|
145
|
+
super
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# We map project modules for a given role or roles
|
150
|
+
#
|
151
|
+
def roles_for(*roles, &block)
|
152
|
+
raise Padrino::Admin::AccessControlError, "Role #{role} must be present and must be a symbol!" if roles.any? { |r| !r.kind_of?(Symbol) } || roles.empty?
|
153
|
+
raise Padrino::Admin::AccessControlError, "You can't merge :any with other roles" if roles.size > 1 && roles.any? { |r| r == :any }
|
154
|
+
|
155
|
+
if roles == [:any]
|
156
|
+
@authorizations << Authorization.new(&block)
|
157
|
+
else
|
158
|
+
raise Padrino::Admin::AccessControlError, "For use custom roles you need to define an Account Class" unless defined?(Account)
|
159
|
+
@roles.concat(roles)
|
160
|
+
@mappers << Proc.new { |account| Mapper.new(account, *roles, &block) }
|
161
|
+
end
|
162
|
+
end
|
141
163
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
164
|
+
##
|
165
|
+
# Returns (allowed && denied paths).
|
166
|
+
# If an account given we also give allowed & denied paths for their role.
|
167
|
+
#
|
168
|
+
def auths(account=nil)
|
169
|
+
if account
|
170
|
+
cache[account.id] ||= Auths.new(@authorizations, @mappers, account)
|
171
|
+
else
|
172
|
+
cache[:any] ||= Auths.new(@authorizations)
|
173
|
+
end
|
174
|
+
end
|
146
175
|
end
|
176
|
+
end # Base
|
147
177
|
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
def roles_for(*roles, &block)
|
152
|
-
raise Padrino::AccessControlError, "Role #{role} must be present and must be a symbol!" if roles.any? { |r| !r.kind_of?(Symbol) } || roles.empty?
|
153
|
-
raise Padrino::AccessControlError, "You can't merge :any with other roles" if roles.size > 1 && roles.any? { |r| r == :any }
|
178
|
+
class Auths #:nodoc:
|
179
|
+
attr_reader :allowed, :denied, :project_modules
|
154
180
|
|
155
|
-
|
156
|
-
|
181
|
+
def initialize(authorizations, mappers=nil, account=nil) #:nodoc:
|
182
|
+
@allowed, @denied = [], []
|
183
|
+
unless authorizations.empty?
|
184
|
+
@allowed = authorizations.collect(&:allowed).flatten
|
185
|
+
@denied = authorizations.collect(&:denied).flatten
|
186
|
+
end
|
187
|
+
if mappers && !mappers.empty?
|
188
|
+
maps = mappers.collect { |m| m.call(account) }.reject { |m| !m.allowed? }
|
189
|
+
@allowed.concat(maps.collect(&:allowed).flatten)
|
190
|
+
@denied.concat(maps.collect(&:denied).flatten)
|
191
|
+
@project_modules = maps.collect(&:project_modules).flatten.uniq
|
157
192
|
else
|
158
|
-
|
159
|
-
@roles.concat(roles)
|
160
|
-
@mappers << Proc.new { |account| Mapper.new(account, *roles, &block) }
|
193
|
+
@project_modules = []
|
161
194
|
end
|
195
|
+
@allowed.uniq!
|
196
|
+
@denied.uniq!
|
162
197
|
end
|
163
198
|
|
164
199
|
##
|
165
|
-
#
|
166
|
-
# If an account given we also give allowed & denied paths for their role.
|
200
|
+
# Return true if the requested path (like request.path_info) is allowed.
|
167
201
|
#
|
168
|
-
def
|
169
|
-
if
|
170
|
-
|
171
|
-
|
172
|
-
cache[:any] ||= Auths.new(@authorizations)
|
173
|
-
end
|
202
|
+
def can?(request_path)
|
203
|
+
return true if @allowed.empty?
|
204
|
+
request_path = "/" if request_path.blank?
|
205
|
+
@allowed.any? { |path| request_path =~ /^#{path}/ } && !cannot?(request_path)
|
174
206
|
end
|
175
|
-
end
|
176
|
-
end # Base
|
177
|
-
|
178
|
-
class Auths #:nodoc:
|
179
|
-
attr_reader :allowed, :denied, :project_modules
|
180
207
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
@denied
|
186
|
-
|
187
|
-
|
188
|
-
maps = mappers.collect { |m| m.call(account) }.reject { |m| !m.allowed? }
|
189
|
-
@allowed.concat(maps.collect(&:allowed).flatten)
|
190
|
-
@denied.concat(maps.collect(&:denied).flatten)
|
191
|
-
@project_modules = maps.collect(&:project_modules).flatten.uniq
|
192
|
-
else
|
193
|
-
@project_modules = []
|
208
|
+
##
|
209
|
+
# Return true if the requested path (like request.path_info) is +not+ allowed.
|
210
|
+
#
|
211
|
+
def cannot?(request_path)
|
212
|
+
return false if @denied.empty?
|
213
|
+
request_path = "/" if request_path.blank?
|
214
|
+
@denied.any? { |path| request_path =~ /^#{path}/ }
|
194
215
|
end
|
195
|
-
|
196
|
-
@denied.uniq!
|
197
|
-
end
|
216
|
+
end # Auths
|
198
217
|
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
def can?(request_path)
|
203
|
-
return true if @allowed.empty?
|
204
|
-
request_path = "/" if request_path.blank?
|
205
|
-
@allowed.any? { |path| request_path =~ /^#{path}/ } && !cannot?(request_path)
|
206
|
-
end
|
218
|
+
class Authorization
|
219
|
+
attr_reader :allowed, :denied
|
207
220
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
request_path = "/" if request_path.blank?
|
214
|
-
@denied.any? { |path| request_path =~ /^#{path}/ }
|
215
|
-
end
|
216
|
-
end # Auths
|
221
|
+
def initialize(&block) #:nodoc:
|
222
|
+
@allowed = []
|
223
|
+
@denied = []
|
224
|
+
yield self
|
225
|
+
end
|
217
226
|
|
218
|
-
|
219
|
-
|
227
|
+
##
|
228
|
+
# Allow a specified path
|
229
|
+
#
|
230
|
+
def allow(path)
|
231
|
+
@allowed << path unless @allowed.include?(path)
|
232
|
+
end
|
220
233
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
234
|
+
##
|
235
|
+
# Deny a specified path
|
236
|
+
#
|
237
|
+
def require_login(path)
|
238
|
+
@denied << path unless @denied.include?(path)
|
239
|
+
end
|
240
|
+
alias :deny :require_login
|
241
|
+
end # Authorization
|
226
242
|
|
227
|
-
|
228
|
-
|
229
|
-
#
|
230
|
-
def allow(path)
|
231
|
-
@allowed << path unless @allowed.include?(path)
|
232
|
-
end
|
243
|
+
class Mapper
|
244
|
+
attr_reader :project_modules, :roles, :denied
|
233
245
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
class Mapper
|
244
|
-
attr_reader :project_modules, :roles, :denied
|
245
|
-
|
246
|
-
def initialize(account, *roles, &block) #:nodoc:
|
247
|
-
@project_modules = []
|
248
|
-
@allowed = []
|
249
|
-
@denied = []
|
250
|
-
@roles = roles
|
251
|
-
@account = account.dup
|
252
|
-
yield(self, @account)
|
253
|
-
end
|
246
|
+
def initialize(account, *roles, &block) #:nodoc:
|
247
|
+
@project_modules = []
|
248
|
+
@allowed = []
|
249
|
+
@denied = []
|
250
|
+
@roles = roles
|
251
|
+
@account = account.dup
|
252
|
+
yield(self, @account)
|
253
|
+
end
|
254
254
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
255
|
+
##
|
256
|
+
# Create a new project module
|
257
|
+
#
|
258
|
+
def project_module(name, path=nil, &block)
|
259
|
+
@project_modules << ProjectModule.new(name, path, &block)
|
260
|
+
end
|
261
261
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
262
|
+
##
|
263
|
+
# Globally allow an paths for the current role
|
264
|
+
#
|
265
|
+
def allow(path)
|
266
|
+
@allowed << path unless @allowed.include?(path)
|
267
|
+
end
|
268
268
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
269
|
+
##
|
270
|
+
# Globally deny an pathsfor the current role
|
271
|
+
#
|
272
|
+
def deny(path)
|
273
|
+
@denied << path unless @allowed.include?(path)
|
274
|
+
end
|
275
275
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
276
|
+
##
|
277
|
+
# Return true if role is included in given roles
|
278
|
+
#
|
279
|
+
def allowed?
|
280
|
+
@roles.any? { |r| r == @account.role.to_s.downcase.to_sym }
|
281
|
+
end
|
282
282
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
283
|
+
##
|
284
|
+
# Return allowed paths
|
285
|
+
#
|
286
|
+
def allowed
|
287
|
+
@project_modules.each { |pm| @allowed.concat(pm.allowed) }
|
288
|
+
@allowed.uniq
|
289
|
+
end
|
290
|
+
end # Mapper
|
291
|
+
|
292
|
+
class ProjectModule
|
293
|
+
attr_reader :name, :menus, :path
|
294
|
+
|
295
|
+
def initialize(name, path=nil, options={}, &block) #:nodoc:
|
296
|
+
@name = name
|
297
|
+
@options = options
|
298
|
+
@allowed = []
|
299
|
+
@menus = []
|
300
|
+
@path = path
|
301
|
+
@allowed << path if path
|
302
|
+
yield self if block_given?
|
303
|
+
end
|
304
304
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
305
|
+
##
|
306
|
+
# Build a new menu and automaitcally add the action on the allowed actions.
|
307
|
+
#
|
308
|
+
def menu(name, path=nil, options={}, &block)
|
309
|
+
@menus << Menu.new(name, path, options, &block)
|
310
|
+
end
|
311
311
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
312
|
+
##
|
313
|
+
# Return allowed controllers
|
314
|
+
#
|
315
|
+
def allowed
|
316
|
+
@menus.each { |m| @allowed.concat(m.allowed) }
|
317
|
+
@allowed.uniq
|
318
|
+
end
|
319
319
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
320
|
+
##
|
321
|
+
# Return the original name or try to translate or humanize the symbol
|
322
|
+
#
|
323
|
+
def human_name
|
324
|
+
@name.is_a?(Symbol) ? I18n.t("admin.menus.#{@name}", :default => @name.to_s.humanize) : @name
|
325
|
+
end
|
326
326
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
327
|
+
##
|
328
|
+
# Return symbol for the given project module
|
329
|
+
#
|
330
|
+
def uid
|
331
|
+
@name.to_s.downcase.gsub(/[^a-z0-9]+/, '').gsub(/-+$/, '').gsub(/^-+$/, '').to_sym
|
332
|
+
end
|
333
333
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
334
|
+
##
|
335
|
+
# Return ExtJs Config for this project module
|
336
|
+
#
|
337
|
+
def config
|
338
|
+
options = @options.merge(:text => human_name)
|
339
|
+
options.merge!(:menu => @menus.collect(&:config)) if @menus.size > 0
|
340
|
+
options.merge!(:handler => Padrino::Admin::Config::Variable.new("function(){ Admin.app.load('#{path}') }")) if @path
|
341
|
+
options
|
342
|
+
end
|
343
|
+
end # ProjectModule
|
344
|
+
|
345
|
+
class Menu
|
346
|
+
attr_reader :name, :options, :items, :path
|
347
|
+
|
348
|
+
def initialize(name, path=nil, options={}, &block) #:nodoc:
|
349
|
+
@name = name
|
350
|
+
@path = path
|
351
|
+
@options = options
|
352
|
+
@allowed = []
|
353
|
+
@items = []
|
354
|
+
@allowed << path if path
|
355
|
+
yield self if block_given?
|
356
|
+
end
|
357
357
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
358
|
+
##
|
359
|
+
# Add a new submenu to the menu
|
360
|
+
#
|
361
|
+
def add(name, path=nil, options={}, &block)
|
362
|
+
@items << Menu.new(name, path, options, &block)
|
363
|
+
end
|
364
364
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
365
|
+
##
|
366
|
+
# Return allowed controllers
|
367
|
+
#
|
368
|
+
def allowed
|
369
|
+
@items.each { |i| @allowed.concat(i.allowed) }
|
370
|
+
@allowed.uniq
|
371
|
+
end
|
372
372
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
373
|
+
##
|
374
|
+
# Return the original name or try to translate or humanize the symbol
|
375
|
+
#
|
376
|
+
def human_name
|
377
|
+
@name.is_a?(Symbol) ? I18n.t("admin.menus.#{@name}", :default => @name.to_s.humanize) : @name
|
378
|
+
end
|
379
379
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
380
|
+
##
|
381
|
+
# Return a unique id for the given project module
|
382
|
+
#
|
383
|
+
def uid
|
384
|
+
@name.to_s.downcase.gsub(/[^a-z0-9]+/, '').gsub(/-+$/, '').gsub(/^-+$/, '').to_sym
|
385
|
+
end
|
386
386
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
387
|
+
##
|
388
|
+
# Return ExtJs Config for this menu
|
389
|
+
#
|
390
|
+
def config
|
391
|
+
if @path.blank? && @items.empty?
|
392
|
+
options = human_name
|
393
|
+
else
|
394
|
+
options = @options.merge(:text => human_name)
|
395
|
+
options.merge!(:menu => @items.collect(&:config)) if @items.size > 0
|
396
|
+
options.merge!(:handler => "function(){ Admin.app.load('#{path}') }".to_l) if @path
|
397
|
+
end
|
398
|
+
options
|
397
399
|
end
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
end # AccessControl
|
400
|
+
end # Menu
|
401
|
+
end # AccessControl
|
402
|
+
end # Admin
|
402
403
|
end # Padrino
|
data/lib/padrino-admin/config.rb
CHANGED
@@ -13,7 +13,7 @@ module Padrino
|
|
13
13
|
#
|
14
14
|
# { "function": "alert('Test')" }
|
15
15
|
#
|
16
|
-
# But if in our javascript need to "eval" this function is not possible because
|
16
|
+
# But if in our javascript we need to "eval" this function is not possible because
|
17
17
|
# it's a string.
|
18
18
|
#
|
19
19
|
# Using Padrino::Config::Variable the result will be:
|
@@ -38,7 +38,7 @@ module Padrino
|
|
38
38
|
##
|
39
39
|
# Returns true if the +current_account+ is allowed to see the requested path
|
40
40
|
#
|
41
|
-
# For configure this role please refer to: +Padrino::AccessControl::Base+
|
41
|
+
# For configure this role please refer to: +Padrino::Admin::AccessControl::Base+
|
42
42
|
#
|
43
43
|
def allowed?
|
44
44
|
access_control.auths(current_account).can?(request.path_info)
|
@@ -86,10 +86,10 @@ module Padrino
|
|
86
86
|
##
|
87
87
|
# This method generates a new ExtJs BoxComponent.
|
88
88
|
#
|
89
|
-
#
|
89
|
+
# ==== Examples
|
90
90
|
#
|
91
|
-
#
|
92
|
-
#
|
91
|
+
# -box "My Title", "My Subtitle", :submit => true, :collapsible => true, :style => "padding:none", :start => :close do
|
92
|
+
# my content
|
93
93
|
#
|
94
94
|
# Defaults:
|
95
95
|
#
|
@@ -155,7 +155,8 @@ module Padrino
|
|
155
155
|
# :function:: name of the function. Default is autogenerated.
|
156
156
|
# :multiple:: if true returns a collections of +:value+. Default false.
|
157
157
|
#
|
158
|
-
#
|
158
|
+
# ==== Examples multiple values:
|
159
|
+
#
|
159
160
|
# # Generate:
|
160
161
|
# # <script type="text/javascript">
|
161
162
|
# # function showAccountImages(){
|
@@ -276,7 +277,7 @@ module Padrino
|
|
276
277
|
[content_tag(:script, javascript, :type => 'text/javascript'), container, tag(:div, :class => :clear)].join("\n")
|
277
278
|
end
|
278
279
|
|
279
|
-
module AbstractFormBuilder
|
280
|
+
module AbstractFormBuilder #:nodoc:
|
280
281
|
# f.open_window_grid :upload_ids, :brand_ids, :with => :brands, :get => :id, :show => :name
|
281
282
|
def open_window_grid(field, options={})
|
282
283
|
@template.open_window_grid object_name, field, options
|
data/lib/padrino-admin/orm.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
module Padrino
|
2
2
|
module Admin
|
3
3
|
|
4
|
+
##
|
5
|
+
# Extend your orm adding these functions:
|
6
|
+
#
|
7
|
+
# * Translatable columns names (if not just support them)
|
8
|
+
# * Full text search
|
9
|
+
# * Pagination
|
10
|
+
# * Extend Account Model with: role, validation, permission, authentication
|
11
|
+
#
|
4
12
|
module Orm
|
5
13
|
|
6
14
|
class ExtSearch < Struct.new(:count, :records); end
|
@@ -26,7 +26,7 @@ module Padrino
|
|
26
26
|
##
|
27
27
|
# Transforms attribute key names into a more humane format, such as "First name" instead of "first_name".
|
28
28
|
#
|
29
|
-
#
|
29
|
+
# ==== Examples
|
30
30
|
# Person.human_attribute_name("first_name") # => "First name"
|
31
31
|
#
|
32
32
|
# Specify +options+ with additional translating options.
|
@@ -50,7 +50,7 @@ module Padrino
|
|
50
50
|
##
|
51
51
|
# Transforms attribute key names into a more humane format, such as "First name" instead of "first_name".
|
52
52
|
#
|
53
|
-
#
|
53
|
+
# ==== Examples
|
54
54
|
# Person.human_attribute_name("first_name") # => "First name"
|
55
55
|
#
|
56
56
|
# Specify +options+ with additional translating options.
|
data/padrino-admin.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{padrino-admin}
|
8
|
-
s.version = "0.7.
|
8
|
+
s.version = "0.7.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Padrino Team", "Nathan Esquenazi", "Davide D'Agostino", "Arthur Chiu"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-10}
|
13
13
|
s.description = %q{Admin View for Padrino applications}
|
14
14
|
s.email = %q{padrinorb@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -356,9 +356,9 @@ Gem::Specification.new do |s|
|
|
356
356
|
|
357
357
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
358
358
|
s.add_runtime_dependency(%q<json_pure>, [">= 1.2.0"])
|
359
|
-
s.add_runtime_dependency(%q<padrino-core>, ["= 0.7.
|
360
|
-
s.add_runtime_dependency(%q<padrino-gen>, ["= 0.7.
|
361
|
-
s.add_runtime_dependency(%q<padrino-helpers>, ["= 0.7.
|
359
|
+
s.add_runtime_dependency(%q<padrino-core>, ["= 0.7.6"])
|
360
|
+
s.add_runtime_dependency(%q<padrino-gen>, ["= 0.7.6"])
|
361
|
+
s.add_runtime_dependency(%q<padrino-helpers>, ["= 0.7.6"])
|
362
362
|
s.add_runtime_dependency(%q<tilt>, [">= 0.4"])
|
363
363
|
s.add_development_dependency(%q<haml>, [">= 2.2.1"])
|
364
364
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
@@ -367,9 +367,9 @@ Gem::Specification.new do |s|
|
|
367
367
|
s.add_development_dependency(%q<webrat>, [">= 0.5.1"])
|
368
368
|
else
|
369
369
|
s.add_dependency(%q<json_pure>, [">= 1.2.0"])
|
370
|
-
s.add_dependency(%q<padrino-core>, ["= 0.7.
|
371
|
-
s.add_dependency(%q<padrino-gen>, ["= 0.7.
|
372
|
-
s.add_dependency(%q<padrino-helpers>, ["= 0.7.
|
370
|
+
s.add_dependency(%q<padrino-core>, ["= 0.7.6"])
|
371
|
+
s.add_dependency(%q<padrino-gen>, ["= 0.7.6"])
|
372
|
+
s.add_dependency(%q<padrino-helpers>, ["= 0.7.6"])
|
373
373
|
s.add_dependency(%q<tilt>, [">= 0.4"])
|
374
374
|
s.add_dependency(%q<haml>, [">= 2.2.1"])
|
375
375
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
@@ -379,9 +379,9 @@ Gem::Specification.new do |s|
|
|
379
379
|
end
|
380
380
|
else
|
381
381
|
s.add_dependency(%q<json_pure>, [">= 1.2.0"])
|
382
|
-
s.add_dependency(%q<padrino-core>, ["= 0.7.
|
383
|
-
s.add_dependency(%q<padrino-gen>, ["= 0.7.
|
384
|
-
s.add_dependency(%q<padrino-helpers>, ["= 0.7.
|
382
|
+
s.add_dependency(%q<padrino-core>, ["= 0.7.6"])
|
383
|
+
s.add_dependency(%q<padrino-gen>, ["= 0.7.6"])
|
384
|
+
s.add_dependency(%q<padrino-helpers>, ["= 0.7.6"])
|
385
385
|
s.add_dependency(%q<tilt>, [">= 0.4"])
|
386
386
|
s.add_dependency(%q<haml>, [">= 2.2.1"])
|
387
387
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
data/test/test_access_control.rb
CHANGED
@@ -4,7 +4,7 @@ class TestAccessControl < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
def setup
|
6
6
|
load_fixture 'data_mapper'
|
7
|
-
@access = Class.new(Padrino::AccessControl::Base)
|
7
|
+
@access = Class.new(Padrino::Admin::AccessControl::Base)
|
8
8
|
|
9
9
|
@access.roles_for :any do |role|
|
10
10
|
role.allow "/sessions"
|
@@ -38,7 +38,7 @@ class TestAccessControl < Test::Unit::TestCase
|
|
38
38
|
context 'for authorization functionality' do
|
39
39
|
|
40
40
|
should 'check empty auths' do
|
41
|
-
empty = Class.new(Padrino::AccessControl::Base)
|
41
|
+
empty = Class.new(Padrino::Admin::AccessControl::Base)
|
42
42
|
assert empty.auths.can?("/foo/bar")
|
43
43
|
assert ! empty.auths.cannot?("/foo/bar")
|
44
44
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: padrino-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Padrino Team
|
@@ -12,7 +12,7 @@ autorequire:
|
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
14
|
|
15
|
-
date: 2010-02-
|
15
|
+
date: 2010-02-10 00:00:00 +01:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
requirements:
|
34
34
|
- - "="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: 0.7.
|
36
|
+
version: 0.7.6
|
37
37
|
version:
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: padrino-gen
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - "="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.7.
|
46
|
+
version: 0.7.6
|
47
47
|
version:
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: padrino-helpers
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
requirements:
|
54
54
|
- - "="
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 0.7.
|
56
|
+
version: 0.7.6
|
57
57
|
version:
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: tilt
|