express_access 1.0.0.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +87 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/express_access/admin.js +1 -0
- data/app/assets/stylesheets/express_access/admin.css +5 -0
- data/app/assets/stylesheets/express_access/application.css +15 -0
- data/app/assets/stylesheets/express_access/main.sass +3 -0
- data/app/assets/stylesheets/express_access/sections/_role_dashboard.sass +29 -0
- data/app/assets/stylesheets/express_access.css +4 -0
- data/app/controllers/express_access/permissions_controller.rb +4 -0
- data/app/controllers/express_access/roles_controller.rb +14 -0
- data/app/controllers/express_access/routes_controller.rb +30 -0
- data/app/controllers/express_access/users_controller.rb +21 -0
- data/app/helpers/express_access/application_helper.rb +4 -0
- data/app/helpers/express_access/permissions_helper.rb +4 -0
- data/app/helpers/express_access/roles_helper.rb +4 -0
- data/app/models/express_access/audit_log.rb +27 -0
- data/app/models/express_access/permission.rb +130 -0
- data/app/models/express_access/role.rb +75 -0
- data/app/models/express_access/role_permission.rb +6 -0
- data/app/models/express_access/user_permission.rb +7 -0
- data/app/models/express_access/user_role.rb +7 -0
- data/app/views/express_access/permissions/index.html.et +13 -0
- data/app/views/express_access/permissions/show.html.et +33 -0
- data/app/views/express_access/roles/index.html.et +9 -0
- data/app/views/express_access/roles/show.html.et +68 -0
- data/app/views/express_access/routes/index.html.et +20 -0
- data/app/views/express_access/routes/show.html.et +46 -0
- data/app/views/express_access/users/index.html.et +26 -0
- data/app/views/express_access/users/show.html.et +55 -0
- data/app/views/layouts/express_access/admin.html.et +1 -0
- data/app/views/layouts/express_access/application.html.erb +14 -0
- data/config/initializers/mount_engine.rb +3 -0
- data/config/menu.yml +18 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20141029223053_create_express_access_roles.rb +10 -0
- data/db/migrate/20141029223158_create_express_access_permissions.rb +9 -0
- data/db/migrate/20141029223233_create_express_access_role_permissions.rb +10 -0
- data/db/migrate/20141029223250_create_express_access_user_permissions.rb +10 -0
- data/db/migrate/20150528222337_create_express_access_user_roles.rb +9 -0
- data/db/migrate/20150609124815_add_description_to_role.rb +5 -0
- data/db/migrate/20150914023030_create_express_access_audit_logs.rb +15 -0
- data/db/migrate/20150921063153_add_after_sign_in_path_to_role.rb +5 -0
- data/lib/express_access/after_sign_in_filter.rb +7 -0
- data/lib/express_access/authorization_filter.rb +39 -0
- data/lib/express_access/engine.rb +12 -0
- data/lib/express_access/route.rb +127 -0
- data/lib/express_access/user.rb +79 -0
- data/lib/express_access/version.rb +3 -0
- data/lib/express_access.rb +51 -0
- data/lib/generators/express_access/install/USAGE +8 -0
- data/lib/generators/express_access/install/install_generator.rb +10 -0
- data/lib/tasks/express_access_tasks.rake +4 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/posts_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/user.rb +9 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/posts/index.html.erb +0 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +25 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +83 -0
- data/test/dummy/config/environments/test.rb +41 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/devise.rb +259 -0
- data/test/dummy/config/initializers/express_access.rb +1 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/devise.en.yml +60 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +7 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20150525001419_devise_create_users.rb +42 -0
- data/test/dummy/db/schema.rb +82 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/test/fixtures/express_access/permissions.yml +28 -0
- data/test/dummy/test/fixtures/express_access/role_permissions.yml +21 -0
- data/test/dummy/test/fixtures/express_access/roles.yml +27 -0
- data/test/dummy/test/fixtures/express_access/user_permissions.yml +5 -0
- data/test/dummy/test/fixtures/express_access/user_roles.yml +15 -0
- data/test/dummy/test/fixtures/users.yml +19 -0
- data/test/dummy/test/initializer_test.rb +8 -0
- data/test/dummy/test/models/user_test.rb +7 -0
- data/test/express_access_test.rb +7 -0
- data/test/fixtures/express_access/audit_logs.yml +10 -0
- data/test/fixtures/express_access/permissions.yml +28 -0
- data/test/fixtures/express_access/role_permissions.yml +21 -0
- data/test/fixtures/express_access/roles.yml +34 -0
- data/test/fixtures/express_access/user_permissions.yml +5 -0
- data/test/fixtures/express_access/user_roles.yml +19 -0
- data/test/fixtures/users.yml +22 -0
- data/test/helpers/express_access/permissions_helper_test.rb +6 -0
- data/test/helpers/express_access/roles_helper_test.rb +6 -0
- data/test/integration/navigation_test.rb +33 -0
- data/test/lib/authorization_filter_test.rb +64 -0
- data/test/lib/generators/express_access/install/install_generator_test.rb +16 -0
- data/test/models/express_access/audit_log_test.rb +9 -0
- data/test/models/express_access/permission_test.rb +50 -0
- data/test/models/express_access/role_permission_test.rb +9 -0
- data/test/models/express_access/role_test.rb +36 -0
- data/test/models/express_access/user_permission_test.rb +9 -0
- data/test/models/express_access/user_role_test.rb +9 -0
- data/test/models/express_access/user_test.rb +77 -0
- data/test/test_helper.rb +19 -0
- metadata +375 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
pane(
|
|
2
|
+
title: "Routes for #{Rails.application.class.to_s.titleize}",
|
|
3
|
+
status: "#{ExpressAccess::Route.all.count} routes",
|
|
4
|
+
class: ['large-table-container']
|
|
5
|
+
) {
|
|
6
|
+
smart_table(:routes,
|
|
7
|
+
actions: false,
|
|
8
|
+
sortable: ['Path', 'Endpoint', 'Description'],
|
|
9
|
+
columns: {
|
|
10
|
+
"Path" => :path_link,
|
|
11
|
+
"Endpoint" => :endp_link,
|
|
12
|
+
"Description" => :description_link,
|
|
13
|
+
"Applicable Permission" => -> (route) { route.permission ? (helpers.link_to route.permission.name, permission_path(route.permission.id)) : 'Any'},
|
|
14
|
+
"Roles" => -> (route) { route.permission ?
|
|
15
|
+
(route.permission.all_roles.map do |role|
|
|
16
|
+
helpers.link_to role.name, role_path(role.id)
|
|
17
|
+
end.join(", ")) : 'Anyone'
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
pane(title: "Resource: #{route.verb} #{route.path}") {
|
|
2
|
+
dl {
|
|
3
|
+
dt { "Action:" }
|
|
4
|
+
dd { route.description }
|
|
5
|
+
|
|
6
|
+
dt { "Scope:" }
|
|
7
|
+
dd { "#{route.path.split('/').select {|part| part.match(/:/) }.join(', ')}" }
|
|
8
|
+
|
|
9
|
+
dt { "Endpoint:"}
|
|
10
|
+
dd { route.endp }
|
|
11
|
+
|
|
12
|
+
dt { "Protected by:" }
|
|
13
|
+
dd { route.permission ? link_to(route.permission.name, permission_path(route.permission)) : 'Not protected'}
|
|
14
|
+
|
|
15
|
+
dt { "Roles Permitted:" }
|
|
16
|
+
dd { route.permission ? route.permission.all_roles.map {|role| helpers.link_to role.name, role_path(role)}.join(", ").html_safe : "Anyone"}
|
|
17
|
+
|
|
18
|
+
dt { "Access Log:" }
|
|
19
|
+
dd {
|
|
20
|
+
if route.log.any?
|
|
21
|
+
route.log.each do |log|
|
|
22
|
+
if log.any?
|
|
23
|
+
log.each do |day, logs|
|
|
24
|
+
ul(class: 'circle'){
|
|
25
|
+
li { "#{day.strftime("%B %d, %Y")}" }
|
|
26
|
+
logs.each do |each_log|
|
|
27
|
+
ul(class: 'no-bullet') {
|
|
28
|
+
pane(status: each_log.created_at.strftime("%H:%M %Z")) {
|
|
29
|
+
li { "By: #{each_log.user_email}" }
|
|
30
|
+
li { "With Permission: #{each_log.permission_name}"}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
li { "none" }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
"none"
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
main_region {
|
|
2
|
+
h2 { 'Users' }
|
|
3
|
+
smart_table(:users, resource_class: "::User", resource_path: -> (user) { express_access.user_path(user) },
|
|
4
|
+
columns: {
|
|
5
|
+
"Email" => :email_link,
|
|
6
|
+
"Sign In Count" => :sign_in_count,
|
|
7
|
+
"Current Sign in At" => :current_sign_in_at,
|
|
8
|
+
"Last Sign In At" => :last_sign_in_at
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
sidebar_region {
|
|
12
|
+
pane(title: "User") {
|
|
13
|
+
smart_form(:user, resource_class: "::User", collection_path: -> { express_access.users_path },
|
|
14
|
+
resource_path: -> (user) { express_access.user_path(user) },
|
|
15
|
+
virtual: [:password, :password_confirmation],
|
|
16
|
+
exclude: [:encrypted_password,
|
|
17
|
+
:reset_password_token,
|
|
18
|
+
:reset_password_sent_at,
|
|
19
|
+
:remember_created_at,
|
|
20
|
+
:sign_in_count,
|
|
21
|
+
:current_sign_in_at,
|
|
22
|
+
:last_sign_in_at,
|
|
23
|
+
:current_sign_in_ip,
|
|
24
|
+
:last_sign_in_ip])
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
v_box {
|
|
2
|
+
h_box{
|
|
3
|
+
pane(title: "<strong>#{user.email}</strong>".html_safe) {
|
|
4
|
+
dl {
|
|
5
|
+
dt { "User Created At:" }
|
|
6
|
+
dd { user.created_at ? user.created_at.to_date : "unknown"}
|
|
7
|
+
|
|
8
|
+
dt { "Last Sign In:" }
|
|
9
|
+
dd { user.last_sign_in_at ? "#{time_ago_in_words(user.last_sign_in_at) << ' ago'}" : "never"}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
v_box{
|
|
13
|
+
pane(title: "Roles of #{user.email}") {
|
|
14
|
+
smart_table(:user_roles,
|
|
15
|
+
collection: -> { user.user_roles },
|
|
16
|
+
columns: {"Name" => -> (role) { helpers.link_to role.name, role_path(role.role_id) },
|
|
17
|
+
"Date Assigned" => :created_at_in_words})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pane(title: "All Permissions for #{user.email}") {
|
|
21
|
+
smart_table(:user_permissions, actions: false,
|
|
22
|
+
collection: -> { user.user_permissions.sort_by(&:name) },
|
|
23
|
+
columns: {
|
|
24
|
+
'Permissions' => -> (permission) { helpers.link_to permission.name, permission_path(permission.permission_id)},
|
|
25
|
+
'Date Assigned' => :created_at_in_words }) # not yet correct date
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pane(title: "Resources Access for #{user.email}", class: 'large-table-container') {
|
|
31
|
+
smart_table(:routes, actions: false,
|
|
32
|
+
collection: -> { user.permissions.flat_map { |permission| permission.all_routes } },
|
|
33
|
+
columns: {
|
|
34
|
+
"Path" => :path_link,
|
|
35
|
+
"Endpoint" => :endp_link,
|
|
36
|
+
"Description" => :description_link
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
v_box {
|
|
42
|
+
pane(title: 'Edit User') {
|
|
43
|
+
smart_form(:user, resource_class: "::User", resource_path: -> (user) { express_access.user_path(user) },
|
|
44
|
+
virtual: [:password, :password_confirmation],
|
|
45
|
+
exclude: [:encrypted_password,
|
|
46
|
+
:reset_password_token,
|
|
47
|
+
:reset_password_sent_at,
|
|
48
|
+
:remember_created_at,
|
|
49
|
+
:sign_in_count,
|
|
50
|
+
:current_sign_in_at,
|
|
51
|
+
:last_sign_in_at,
|
|
52
|
+
:current_sign_in_ip,
|
|
53
|
+
:last_sign_in_ip])
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
render(template: 'layouts/express_admin/admin')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>ExpressAccess</title>
|
|
5
|
+
<%= stylesheet_link_tag "express_access/application", media: "all" %>
|
|
6
|
+
<%= javascript_include_tag "express_access/application" %>
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
|
|
11
|
+
<%= yield %>
|
|
12
|
+
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
data/config/menu.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
title: 'Express Access'
|
|
2
|
+
path: 'express_access.users_path'
|
|
3
|
+
items:
|
|
4
|
+
-
|
|
5
|
+
title: 'Users'
|
|
6
|
+
path: 'express_access.users_path'
|
|
7
|
+
|
|
8
|
+
-
|
|
9
|
+
title: 'Roles'
|
|
10
|
+
path: 'express_access.roles_path'
|
|
11
|
+
|
|
12
|
+
-
|
|
13
|
+
title: 'Permissions'
|
|
14
|
+
path: 'express_access.permissions_path'
|
|
15
|
+
|
|
16
|
+
-
|
|
17
|
+
title: 'Routes'
|
|
18
|
+
path: 'express_access.routes_path'
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateExpressAccessAuditLogs < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :express_access_audit_logs do |t|
|
|
4
|
+
t.string :user_email
|
|
5
|
+
t.string :permission_name
|
|
6
|
+
t.string :request_path
|
|
7
|
+
t.boolean :granted
|
|
8
|
+
t.string :controller_name
|
|
9
|
+
t.string :action_name
|
|
10
|
+
t.string :ip_address
|
|
11
|
+
|
|
12
|
+
t.timestamps null: false
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ExpressAccess
|
|
2
|
+
class AuthorizationFilter
|
|
3
|
+
def self.before(controller)
|
|
4
|
+
if permission = ExpressAccess::Permission.for(controller: controller.request.params[:controller],
|
|
5
|
+
action: controller.request.params[:action],
|
|
6
|
+
path: controller.request.path)
|
|
7
|
+
if controller.current_user
|
|
8
|
+
unless controller.current_user.has?(permission)
|
|
9
|
+
Rails.logger.info("DOES NOT HAVE PERMISSION #{permission.name}")
|
|
10
|
+
AuditLog.create(user_email: controller.current_user.email,
|
|
11
|
+
permission_name: permission.name,
|
|
12
|
+
request_path: controller.request.path,
|
|
13
|
+
granted: false,
|
|
14
|
+
controller_name: controller.controller_path,
|
|
15
|
+
action_name: controller.action_name,
|
|
16
|
+
ip_address: controller.request.env['REMOTE_ADDR'])
|
|
17
|
+
controller.access_authorization_failed!
|
|
18
|
+
else
|
|
19
|
+
Rails.logger.info("HAS PERMISSION #{permission.name}")
|
|
20
|
+
AuditLog.create(user_email: controller.current_user.email,
|
|
21
|
+
permission_name: permission.name,
|
|
22
|
+
request_path: controller.request.path,
|
|
23
|
+
granted: true,
|
|
24
|
+
controller_name: controller.controller_path,
|
|
25
|
+
action_name: controller.action_name,
|
|
26
|
+
ip_address: controller.request.env['REMOTE_ADDR'])
|
|
27
|
+
nil # success
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
if defined?(Devise)
|
|
31
|
+
controller.authenticate_user!
|
|
32
|
+
else
|
|
33
|
+
controller.access_authorization_failed!
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'express_admin'
|
|
2
|
+
|
|
3
|
+
module ExpressAccess
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
ExpressAccess::Engine.config.express_access_mount_point = '/admin/access'
|
|
6
|
+
initializer :assets do |config|
|
|
7
|
+
Rails.application.config.assets.precompile += %w( express_access.css express_access/admin.js express_access/admin.css )
|
|
8
|
+
end
|
|
9
|
+
include ::ExpressAdmin::Menu::Loader
|
|
10
|
+
isolate_namespace ExpressAccess
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'request_store'
|
|
2
|
+
module ExpressAccess
|
|
3
|
+
class Route
|
|
4
|
+
attr :name, :verb, :path, :endp, :reqs
|
|
5
|
+
|
|
6
|
+
def initialize(name, verb, path, endp, reqs = nil)
|
|
7
|
+
@name, @verb, @path, @endp, @reqs = name, verb, path, endp, reqs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.find(id)
|
|
11
|
+
@routes ||= all.inject({}) {|hash, route| hash[route.id] = route; hash}
|
|
12
|
+
@routes[id]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.columns
|
|
16
|
+
[OpenStruct.new(name: :name),
|
|
17
|
+
OpenStruct.new(name: :verb),
|
|
18
|
+
OpenStruct.new(name: :path),
|
|
19
|
+
OpenStruct.new(name: :endp),
|
|
20
|
+
OpenStruct.new(name: :reqs)]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def status_for_role(role)
|
|
24
|
+
if perm = permission
|
|
25
|
+
if role.permissions.include?(perm)
|
|
26
|
+
"granted"
|
|
27
|
+
else
|
|
28
|
+
"denied"
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
""
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def id
|
|
36
|
+
path.slice(1..-1).split('/').unshift(verb).join('-').downcase
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
alias :to_param :id
|
|
40
|
+
|
|
41
|
+
# these should be decorators
|
|
42
|
+
def description
|
|
43
|
+
if action.eql?('index')
|
|
44
|
+
"#{action_phrase.capitalize} #{object_phrase.titleize.pluralize}"
|
|
45
|
+
else
|
|
46
|
+
"#{action_phrase.capitalize} #{object_phrase.titleize}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def article
|
|
51
|
+
(object_phrase.match(/^[aeiou]/) &&
|
|
52
|
+
!object_phrase.match(/^user/)) ? 'an' : 'a'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def action_phrase
|
|
56
|
+
({
|
|
57
|
+
'new' => "view the form for a new",
|
|
58
|
+
'edit' => "view the form to edit #{article}",
|
|
59
|
+
'update' => "update #{article}",
|
|
60
|
+
'show' => "show details for #{article}",
|
|
61
|
+
'destroy'=> "delete #{article}",
|
|
62
|
+
'create' => "create a new",
|
|
63
|
+
'index' => "view a list of"
|
|
64
|
+
}[action] || action.pluralize)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def object_phrase
|
|
68
|
+
controller.split('/').last.singularize rescue 'Unknown'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def controller
|
|
72
|
+
endp.split('#').first || 'None'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def action
|
|
76
|
+
endp.split('#').last || 'None'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def permission
|
|
80
|
+
@permission ||= Permission.for(controller: controller, action: action, path: path)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def log
|
|
84
|
+
@log ||= AuditLog.for(endp)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class Formatter
|
|
88
|
+
attr :routes
|
|
89
|
+
attr :engines
|
|
90
|
+
def initialize
|
|
91
|
+
@routes = []
|
|
92
|
+
@engine_paths = {}
|
|
93
|
+
end
|
|
94
|
+
def result
|
|
95
|
+
@routes
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def header(routes) ; end
|
|
99
|
+
def section_title(title) ; end
|
|
100
|
+
def no_routes ; end
|
|
101
|
+
|
|
102
|
+
def section(routes)
|
|
103
|
+
@routes += routes.map do |route|
|
|
104
|
+
endpoint = route[:reqs].match(/^(\S+)/).try(:[], 0)
|
|
105
|
+
requirements = route[:reqs].match(/^\S+\s(.*)/).try(:[], 1)
|
|
106
|
+
path = route[:path].gsub(/\(\.:format\)/, '')
|
|
107
|
+
if possible_engine = (endpoint.classify.constantize rescue nil)
|
|
108
|
+
if possible_engine.ancestors.include?(::Rails::Engine)
|
|
109
|
+
@engine_paths[endpoint.split("::").first.underscore] = path
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
if engine_path = @engine_paths[endpoint.split(/[\#\/]/).first]
|
|
113
|
+
path = [engine_path, path].join unless engine_path.eql?('/')
|
|
114
|
+
end
|
|
115
|
+
Route.new(route[:name], route[:verb], path, endpoint, requirements)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.all
|
|
121
|
+
RequestStore.store[:all_routes] ||= begin
|
|
122
|
+
inspector = ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.set)
|
|
123
|
+
inspector.format(Formatter.new)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
module ExpressAccess
|
|
3
|
+
module User
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
include InstanceMethods
|
|
7
|
+
|
|
8
|
+
has_many :user_permissions, class_name: "::ExpressAccess::UserPermission"
|
|
9
|
+
|
|
10
|
+
has_many :direct_permissions, through: :user_permissions,
|
|
11
|
+
source: :permission,
|
|
12
|
+
class_name: "::ExpressAccess::Permission"
|
|
13
|
+
|
|
14
|
+
has_many :user_roles, class_name: "::ExpressAccess::UserRole"
|
|
15
|
+
|
|
16
|
+
has_many :roles, through: :user_roles,
|
|
17
|
+
source: :role,
|
|
18
|
+
class_name: "::ExpressAccess::Role"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module InstanceMethods
|
|
23
|
+
|
|
24
|
+
def role_permissions
|
|
25
|
+
roles.map(&:permissions).flatten
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def permissions
|
|
29
|
+
(direct_permissions + role_permissions).to_set
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def may?(permission_name)
|
|
33
|
+
if Permission[permission_name].nil?
|
|
34
|
+
true
|
|
35
|
+
else
|
|
36
|
+
permissions.map(&:name).include?(permission_name.to_s)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias may_do? may?
|
|
41
|
+
alias may_use? may?
|
|
42
|
+
|
|
43
|
+
def has?(permission)
|
|
44
|
+
raise "not a permission" unless permission.kind_of?(Permission)
|
|
45
|
+
may?(permission.name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def method_missing(*args)
|
|
50
|
+
name = args.first
|
|
51
|
+
if (action = name.to_s.match(/^may_(\w+)?/).try(:[], 1)) && args.size.eql?(2)
|
|
52
|
+
may?("#{args[1]}##{action}")
|
|
53
|
+
else
|
|
54
|
+
super(*args)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def name
|
|
59
|
+
super.send(:name) rescue email
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def after_sign_in_path
|
|
63
|
+
return nil if role_with_after_sign_in_paths.nil?
|
|
64
|
+
|
|
65
|
+
role_with_after_sign_in_paths.after_sign_in_path
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# The decision to keep this simple for now assume two things:
|
|
71
|
+
# 1. The user, more often than not, would only contain the most specific role. This means that it is unlikely that a user would have roles which have an ancestor-descendant relationship with each other. It would get a bit more complex though when we think about adding direct permissions to the roles.
|
|
72
|
+
# 2. If we have roles which are unrelated to each other and is possessed by a user, we wouldn't really be able to determine the user's preffered after_sign_in_path. This means that we just choose from any of those. We could also allow a per-user after_sign_in_path setting if possible for better fine tuning.
|
|
73
|
+
|
|
74
|
+
def role_with_after_sign_in_paths
|
|
75
|
+
roles.where.not(after_sign_in_path: nil).first
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require "express_access/engine"
|
|
2
|
+
require 'express_access/user'
|
|
3
|
+
require 'express_access/authorization_filter'
|
|
4
|
+
require 'express_access/route'
|
|
5
|
+
require 'express_access/after_sign_in_filter'
|
|
6
|
+
|
|
7
|
+
module ExpressAccess
|
|
8
|
+
def self.initialize!
|
|
9
|
+
setup!
|
|
10
|
+
|
|
11
|
+
ActionDispatch::Reloader.to_prepare do
|
|
12
|
+
ExpressAccess.setup!
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.initialize_user!
|
|
17
|
+
User unless defined? ::User
|
|
18
|
+
|
|
19
|
+
::User.include ExpressAccess::User unless ::User.ancestors.include?(ExpressAccess::User)
|
|
20
|
+
puts "Added ExpressAccess::User to User" unless Rails.env.test?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.initialize_filter!
|
|
24
|
+
unless ActionController::Base.method_defined? 'access_authorization_failed!'
|
|
25
|
+
ActionController::Base.class_eval do
|
|
26
|
+
before_filter ExpressAccess::AuthorizationFilter
|
|
27
|
+
|
|
28
|
+
def access_authorization_failed!
|
|
29
|
+
render :not_authorized, body: "Not authorized"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
puts "Added ExpressAccess::AuthorizationFilter to ActionController::Base" unless Rails.env.test?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.initialize_after_sign_in_filter!
|
|
37
|
+
return nil if !defined?(Devise)
|
|
38
|
+
|
|
39
|
+
ApplicationController.include ExpressAccess::AfterSignInFilter
|
|
40
|
+
puts "Added ExpressAccess::AfterSignInFilter to ApplicationController" unless Rails.env.test?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def self.setup!
|
|
46
|
+
initialize_user!
|
|
47
|
+
initialize_filter!
|
|
48
|
+
initialize_after_sign_in_filter!
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class ExpressAccess::InstallGenerator < Rails::Generators::Base
|
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
3
|
+
|
|
4
|
+
desc "install express_access engine"
|
|
5
|
+
def install
|
|
6
|
+
rake "express_access:install:migrations"
|
|
7
|
+
create_file "config/initializers/express_access.rb", "ExpressAccess.initialize!"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
== README
|
|
2
|
+
|
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
|
4
|
+
application up and running.
|
|
5
|
+
|
|
6
|
+
Things you may want to cover:
|
|
7
|
+
|
|
8
|
+
* Ruby version
|
|
9
|
+
|
|
10
|
+
* System dependencies
|
|
11
|
+
|
|
12
|
+
* Configuration
|
|
13
|
+
|
|
14
|
+
* Database creation
|
|
15
|
+
|
|
16
|
+
* Database initialization
|
|
17
|
+
|
|
18
|
+
* How to run the test suite
|
|
19
|
+
|
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
|
21
|
+
|
|
22
|
+
* Deployment instructions
|
|
23
|
+
|
|
24
|
+
* ...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
|
28
|
+
<tt>rake doc:app</tt>.
|
data/test/dummy/Rakefile
ADDED