express_access 1.0.0.a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +87 -0
  3. data/Rakefile +34 -0
  4. data/app/assets/javascripts/express_access/admin.js +1 -0
  5. data/app/assets/stylesheets/express_access/admin.css +5 -0
  6. data/app/assets/stylesheets/express_access/application.css +15 -0
  7. data/app/assets/stylesheets/express_access/main.sass +3 -0
  8. data/app/assets/stylesheets/express_access/sections/_role_dashboard.sass +29 -0
  9. data/app/assets/stylesheets/express_access.css +4 -0
  10. data/app/controllers/express_access/permissions_controller.rb +4 -0
  11. data/app/controllers/express_access/roles_controller.rb +14 -0
  12. data/app/controllers/express_access/routes_controller.rb +30 -0
  13. data/app/controllers/express_access/users_controller.rb +21 -0
  14. data/app/helpers/express_access/application_helper.rb +4 -0
  15. data/app/helpers/express_access/permissions_helper.rb +4 -0
  16. data/app/helpers/express_access/roles_helper.rb +4 -0
  17. data/app/models/express_access/audit_log.rb +27 -0
  18. data/app/models/express_access/permission.rb +130 -0
  19. data/app/models/express_access/role.rb +75 -0
  20. data/app/models/express_access/role_permission.rb +6 -0
  21. data/app/models/express_access/user_permission.rb +7 -0
  22. data/app/models/express_access/user_role.rb +7 -0
  23. data/app/views/express_access/permissions/index.html.et +13 -0
  24. data/app/views/express_access/permissions/show.html.et +33 -0
  25. data/app/views/express_access/roles/index.html.et +9 -0
  26. data/app/views/express_access/roles/show.html.et +68 -0
  27. data/app/views/express_access/routes/index.html.et +20 -0
  28. data/app/views/express_access/routes/show.html.et +46 -0
  29. data/app/views/express_access/users/index.html.et +26 -0
  30. data/app/views/express_access/users/show.html.et +55 -0
  31. data/app/views/layouts/express_access/admin.html.et +1 -0
  32. data/app/views/layouts/express_access/application.html.erb +14 -0
  33. data/config/initializers/mount_engine.rb +3 -0
  34. data/config/menu.yml +18 -0
  35. data/config/routes.rb +6 -0
  36. data/db/migrate/20141029223053_create_express_access_roles.rb +10 -0
  37. data/db/migrate/20141029223158_create_express_access_permissions.rb +9 -0
  38. data/db/migrate/20141029223233_create_express_access_role_permissions.rb +10 -0
  39. data/db/migrate/20141029223250_create_express_access_user_permissions.rb +10 -0
  40. data/db/migrate/20150528222337_create_express_access_user_roles.rb +9 -0
  41. data/db/migrate/20150609124815_add_description_to_role.rb +5 -0
  42. data/db/migrate/20150914023030_create_express_access_audit_logs.rb +15 -0
  43. data/db/migrate/20150921063153_add_after_sign_in_path_to_role.rb +5 -0
  44. data/lib/express_access/after_sign_in_filter.rb +7 -0
  45. data/lib/express_access/authorization_filter.rb +39 -0
  46. data/lib/express_access/engine.rb +12 -0
  47. data/lib/express_access/route.rb +127 -0
  48. data/lib/express_access/user.rb +79 -0
  49. data/lib/express_access/version.rb +3 -0
  50. data/lib/express_access.rb +51 -0
  51. data/lib/generators/express_access/install/USAGE +8 -0
  52. data/lib/generators/express_access/install/install_generator.rb +10 -0
  53. data/lib/tasks/express_access_tasks.rake +4 -0
  54. data/test/dummy/README.rdoc +28 -0
  55. data/test/dummy/Rakefile +6 -0
  56. data/test/dummy/app/assets/javascripts/application.js +13 -0
  57. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  58. data/test/dummy/app/controllers/application_controller.rb +5 -0
  59. data/test/dummy/app/controllers/posts_controller.rb +4 -0
  60. data/test/dummy/app/helpers/application_helper.rb +2 -0
  61. data/test/dummy/app/models/user.rb +9 -0
  62. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  63. data/test/dummy/app/views/posts/index.html.erb +0 -0
  64. data/test/dummy/bin/bundle +3 -0
  65. data/test/dummy/bin/rails +4 -0
  66. data/test/dummy/bin/rake +4 -0
  67. data/test/dummy/config/application.rb +25 -0
  68. data/test/dummy/config/boot.rb +5 -0
  69. data/test/dummy/config/database.yml +25 -0
  70. data/test/dummy/config/environment.rb +5 -0
  71. data/test/dummy/config/environments/development.rb +37 -0
  72. data/test/dummy/config/environments/production.rb +83 -0
  73. data/test/dummy/config/environments/test.rb +41 -0
  74. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  75. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  76. data/test/dummy/config/initializers/devise.rb +259 -0
  77. data/test/dummy/config/initializers/express_access.rb +1 -0
  78. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  79. data/test/dummy/config/initializers/inflections.rb +16 -0
  80. data/test/dummy/config/initializers/mime_types.rb +4 -0
  81. data/test/dummy/config/initializers/session_store.rb +3 -0
  82. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  83. data/test/dummy/config/locales/devise.en.yml +60 -0
  84. data/test/dummy/config/locales/en.yml +23 -0
  85. data/test/dummy/config/routes.rb +7 -0
  86. data/test/dummy/config/secrets.yml +22 -0
  87. data/test/dummy/config.ru +4 -0
  88. data/test/dummy/db/migrate/20150525001419_devise_create_users.rb +42 -0
  89. data/test/dummy/db/schema.rb +82 -0
  90. data/test/dummy/public/404.html +67 -0
  91. data/test/dummy/public/422.html +67 -0
  92. data/test/dummy/public/500.html +66 -0
  93. data/test/dummy/public/favicon.ico +0 -0
  94. data/test/dummy/test/fixtures/express_access/permissions.yml +28 -0
  95. data/test/dummy/test/fixtures/express_access/role_permissions.yml +21 -0
  96. data/test/dummy/test/fixtures/express_access/roles.yml +27 -0
  97. data/test/dummy/test/fixtures/express_access/user_permissions.yml +5 -0
  98. data/test/dummy/test/fixtures/express_access/user_roles.yml +15 -0
  99. data/test/dummy/test/fixtures/users.yml +19 -0
  100. data/test/dummy/test/initializer_test.rb +8 -0
  101. data/test/dummy/test/models/user_test.rb +7 -0
  102. data/test/express_access_test.rb +7 -0
  103. data/test/fixtures/express_access/audit_logs.yml +10 -0
  104. data/test/fixtures/express_access/permissions.yml +28 -0
  105. data/test/fixtures/express_access/role_permissions.yml +21 -0
  106. data/test/fixtures/express_access/roles.yml +34 -0
  107. data/test/fixtures/express_access/user_permissions.yml +5 -0
  108. data/test/fixtures/express_access/user_roles.yml +19 -0
  109. data/test/fixtures/users.yml +22 -0
  110. data/test/helpers/express_access/permissions_helper_test.rb +6 -0
  111. data/test/helpers/express_access/roles_helper_test.rb +6 -0
  112. data/test/integration/navigation_test.rb +33 -0
  113. data/test/lib/authorization_filter_test.rb +64 -0
  114. data/test/lib/generators/express_access/install/install_generator_test.rb +16 -0
  115. data/test/models/express_access/audit_log_test.rb +9 -0
  116. data/test/models/express_access/permission_test.rb +50 -0
  117. data/test/models/express_access/role_permission_test.rb +9 -0
  118. data/test/models/express_access/role_test.rb +36 -0
  119. data/test/models/express_access/user_permission_test.rb +9 -0
  120. data/test/models/express_access/user_role_test.rb +9 -0
  121. data/test/models/express_access/user_test.rb +77 -0
  122. data/test/test_helper.rb +19 -0
  123. 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>
@@ -0,0 +1,3 @@
1
+ ExpressAdmin::Routes.register do |routes|
2
+ routes.mount ExpressAccess::Engine, at: ExpressAccess::Engine.config.express_access_mount_point
3
+ end
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,6 @@
1
+ ExpressAccess::Engine.routes.draw do
2
+ resources :permissions, except: [:edit]
3
+ resources :users, except: [:edit]
4
+ resources :roles, except: [:edit]
5
+ resources :routes, except: [:edit]
6
+ end
@@ -0,0 +1,10 @@
1
+ class CreateExpressAccessRoles < ActiveRecord::Migration
2
+ def change
3
+ create_table :express_access_roles do |t|
4
+ t.string :name
5
+ t.integer :parent_id
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateExpressAccessPermissions < ActiveRecord::Migration
2
+ def change
3
+ create_table :express_access_permissions do |t|
4
+ t.string :name
5
+
6
+ t.timestamps null: false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateExpressAccessRolePermissions < ActiveRecord::Migration
2
+ def change
3
+ create_table :express_access_role_permissions do |t|
4
+ t.integer :role_id
5
+ t.integer :permission_id
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateExpressAccessUserPermissions < ActiveRecord::Migration
2
+ def change
3
+ create_table :express_access_user_permissions do |t|
4
+ t.integer :user_id
5
+ t.integer :permission_id
6
+
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateExpressAccessUserRoles < ActiveRecord::Migration
2
+ def change
3
+ create_table :express_access_user_roles do |t|
4
+ t.integer :user_id
5
+ t.integer :role_id
6
+ t.timestamps null: false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class AddDescriptionToRole < ActiveRecord::Migration
2
+ def change
3
+ add_column :express_access_roles, :description, :string
4
+ end
5
+ end
@@ -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,5 @@
1
+ class AddAfterSignInPathToRole < ActiveRecord::Migration
2
+ def change
3
+ add_column :express_access_roles, :after_sign_in_path, :string
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module ExpressAccess
2
+ module AfterSignInFilter
3
+ def after_sign_in_path_for(user)
4
+ stored_location_for(user) || user.after_sign_in_path || root_path
5
+ end
6
+ end
7
+ 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,3 @@
1
+ module ExpressAccess
2
+ VERSION = "1.0.0.a"
3
+ 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,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate install Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :express_rbac do
3
+ # # Task goes here
4
+ # 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>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks