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,34 @@
1
+ DEFAULTS: &DEFAULTS
2
+ created_at: <%= Time.now %>
3
+ updated_at: <%= Time.now %>
4
+
5
+ author:
6
+ name: Author
7
+ parent:
8
+ description: An author -- someone who writes.
9
+ after_sign_in_path: "/posts"
10
+ <<: *DEFAULTS
11
+
12
+ editor:
13
+ name: Editor
14
+ parent: author
15
+ description: Editor - someone who edits.
16
+ <<: *DEFAULTS
17
+
18
+ publisher:
19
+ name: Publisher
20
+ parent: author
21
+ description: Publishers - someone who publishes.
22
+ <<: *DEFAULTS
23
+
24
+ admin:
25
+ name: Admin
26
+ parent: publisher
27
+ description: Keys to the kingdom.
28
+ <<: *DEFAULTS
29
+
30
+ expresser:
31
+ name: Expresser
32
+ parent:
33
+ description: Expresser - someone who manages an appexpress site
34
+ <<: *DEFAULTS
@@ -0,0 +1,5 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
+
3
+ editor_specific:
4
+ user: editor
5
+ permission: user_specific_permission
@@ -0,0 +1,19 @@
1
+ admin_admin:
2
+ user: admin
3
+ role: admin
4
+
5
+ author_author:
6
+ user: author
7
+ role: author
8
+
9
+ editor_editor:
10
+ user: editor
11
+ role: editor
12
+
13
+ publisher_publisher:
14
+ user: publisher
15
+ role: publisher
16
+
17
+ expresser_expresser:
18
+ user: expresser
19
+ role: expresser
@@ -0,0 +1,22 @@
1
+ admin:
2
+ email: admin@example.com
3
+ encrypted_password: "$2a$10$3HSyBlfJ2zY3GPxEe1MmC.N8MKsSNYxv/lQR5yEW/ZsCKEzEjU/Vm"
4
+
5
+ author:
6
+ email: author@example.com
7
+ encrypted_password: "$2a$10$3HSyBlfJ2zY3GPxEe1MmC.N8MKsSNYxv/lQR5yEW/ZsCKEzEjU/Vm"
8
+
9
+ editor:
10
+ email: editor@example.com
11
+ encrypted_password: "$2a$10$3HSyBlfJ2zY3GPxEe1MmC.N8MKsSNYxv/lQR5yEW/ZsCKEzEjU/Vm"
12
+
13
+ publisher:
14
+ email: publisher@example.com
15
+ encrypted_password: "$2a$10$3HSyBlfJ2zY3GPxEe1MmC.N8MKsSNYxv/lQR5yEW/ZsCKEzEjU/Vm"
16
+
17
+ nobody:
18
+ email: nobody@example.com
19
+ encrypted_password: "$2a$10$3HSyBlfJ2zY3GPxEe1MmC.N8MKsSNYxv/lQR5yEW/ZsCKEzEjU/Vm"
20
+
21
+ expresser:
22
+ email: expresser@example.com
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class PermissionsHelperTest < ActionView::TestCase
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class RolesHelperTest < ActionView::TestCase
5
+ end
6
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ class NavigationTest < ActionDispatch::IntegrationTest
4
+ fixtures :all
5
+
6
+ test "redirect to login if not logged in" do
7
+ get '/posts'
8
+ assert_redirected_to new_user_session_path
9
+ end
10
+
11
+ test 'current user sees posts if they have permission' do
12
+ post_via_redirect user_session_path,
13
+ "user[email]" => 'editor@example.com',
14
+ "user[password]" => 'asdfasdf'
15
+ assert_equal "/", path
16
+ get '/posts'
17
+ assert_response :success
18
+ end
19
+
20
+ test "current user is redirected to the after_sign_in_path" do
21
+ post_via_redirect user_session_path,
22
+ "user[email]" => "author@example.com",
23
+ "user[password]" => "asdfasdf"
24
+ assert_equal "/posts", path
25
+ end
26
+
27
+ test "current user is redirected to the root_path if user's after_sign_in_path is nil" do
28
+ post_via_redirect user_session_path,
29
+ "user[email]" => "editor@example.com",
30
+ "user[password]" => "asdfasdf"
31
+ assert_equal "/", path
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class AuthorizationFilterTest < ActiveSupport::TestCase
5
+ def setup
6
+ ExpressAccess.initialize_user!
7
+ end
8
+
9
+ def controller(controller, action, path, user)
10
+ OpenStruct.new( request: OpenStruct.new(
11
+ params: {
12
+ controller: controller,
13
+ action: action,
14
+ path: path
15
+ },
16
+ env: '123.123.123.123'
17
+ ),
18
+ current_user: user.nil? ? nil : users(user),
19
+ "access_authorization_failed!" => nil,
20
+ "authenticate_user!" => nil,
21
+ )
22
+ end
23
+
24
+ test 'if controller has current user and if current user has permission' do
25
+ assert_nil ExpressAccess::AuthorizationFilter.before(controller("posts", "edit", "posts/1/edit", :editor))
26
+ end
27
+
28
+ test 'if controller has current user and if current user has no permission' do
29
+ mock_obj = Minitest::Mock.new
30
+ mock_obj.expect(:call, nil)
31
+ c = controller("admin", "index", "admin", :editor)
32
+ c.stub(:access_authorization_failed!, mock_obj) do
33
+ ExpressAccess::AuthorizationFilter.before(c)
34
+ end
35
+ mock_obj.verify
36
+ end
37
+
38
+ test 'if controller has no current user and Devise is not defined' do
39
+ TempDevise = Devise
40
+ Object.send(:remove_const, :Devise)
41
+ assert_nil defined?(Devise)
42
+ mock_obj = Minitest::Mock.new
43
+ mock_obj.expect(:call, nil)
44
+ c = controller("admin", "index", "admin", :editor)
45
+ c.stub(:access_authorization_failed!, mock_obj) do
46
+ ExpressAccess::AuthorizationFilter.before(c)
47
+ end
48
+ mock_obj.verify
49
+ ::Devise = TempDevise
50
+ assert_equal 'constant', defined?(Devise)
51
+ end
52
+
53
+ test 'if controller has no current user and Devise is defined' do
54
+ assert_equal 'constant', defined?(Devise)
55
+ mock_obj = Minitest::Mock.new
56
+ mock_obj.expect(:call, nil)
57
+ c = controller("admin", "index", "admin", nil)
58
+ c.stub(:authenticate_user!, mock_obj) do
59
+ ExpressAccess::AuthorizationFilter.before(c)
60
+ end
61
+ mock_obj.verify
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+ require 'generators/express_access/install/install_generator'
3
+
4
+ module ExpressAccess
5
+ class ExpressAccess::InstallGeneratorTest < Rails::Generators::TestCase
6
+ tests ExpressAccess::InstallGenerator
7
+ destination Rails.root.join('tmp/generators')
8
+ setup :prepare_destination
9
+
10
+ # test "generator runs without errors" do
11
+ # assert_nothing_raised do
12
+ # run_generator ["arguments"]
13
+ # end
14
+ # end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class AuditLogTest < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class PermissionTest < ActiveSupport::TestCase
5
+
6
+ test "Permission.[] looks up permissions" do
7
+ assert Permission[:admin]
8
+ end
9
+
10
+ # test '.for returns the most specific applicable permission'
11
+
12
+ def assert_permission(name, is_for: [])
13
+ named_args = {controller: is_for[0], action: is_for[1], path: is_for[2]}
14
+ assert_equal Permission[name].name, Permission.for(**named_args).try(:name)
15
+ end
16
+
17
+ test 'controller#action specific permissions have priority' do
18
+ assert_permission 'posts#edit', is_for: ['posts', 'edit', '/admin/posts/123/edit']
19
+ assert_permission 'posts#edit', is_for: ['posts', 'edit', '/posts/123/edit']
20
+ assert_permission 'posts#edit', is_for: ['posts', 'edit', 'askdjfklasdfhjk']
21
+
22
+ assert_permission 'posts#publish', is_for: ['posts', 'publish', '/admin/posts/123/publish']
23
+ end
24
+
25
+ test 'resource permission overrides controller#action when path is longer' do
26
+ assert_permission '/posts/999', is_for: ['posts', 'publish', '/posts/999/publish']
27
+ end
28
+
29
+ test 'bare controller permission protects a controller regardless of path' do
30
+ assert_permission 'posts', is_for: ['posts', 'show', '/admin/posts/123']
31
+ assert_permission 'posts', is_for: ['posts', 'whatever', nil]
32
+ assert_permission 'posts', is_for: ['posts', 'index', '/']
33
+ end
34
+
35
+ test 'locked parent path locks sub paths' do
36
+ assert_permission '/accounting', is_for: ['invoices', 'index', '/accounting/invoices']
37
+ assert_permission '/accounting', is_for: ['invoices', 'create', '/accounting/invoices']
38
+ end
39
+
40
+ test 'sub path permission takes precedence over parent path permission' do
41
+ assert_permission '/accounting/gl', is_for: ['gl/entry', 'index', '/accounting/gl/entries']
42
+ assert_permission '/accounting/gl', is_for: ['gl/entry', 'create', '/accounting/gl/entries']
43
+ end
44
+
45
+
46
+ test ".for returns nil for requests that have no applicable permission" do
47
+ assert_nil Permission.for(controller: 'jhasdkfjgha', action: 'index', path: '/asdfkgnqe/')
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class RolePermissionTest < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class RoleTest < ActiveSupport::TestCase
5
+
6
+ test "there are 5 roles" do
7
+ assert_equal 5, Role.count
8
+ end
9
+
10
+ test "#expressers return users with role of expresser" do
11
+ assert_equal 1, Role.expressers.count
12
+ end
13
+
14
+ test "parent relationships are set properly" do
15
+ assert_equal Role[:author], Role[:editor].parent
16
+ assert_equal Role[:author], Role[:publisher].parent
17
+ end
18
+
19
+ test "top_level scope identifies roles without a parent" do
20
+ [Role[:expresser], Role[:author]].each do |role|
21
+ assert_includes Role.top_level, role
22
+ end
23
+ end
24
+
25
+ test "roles have permissions" do
26
+ assert_includes Role[:author].permissions, Permission[:posts]
27
+ assert_not_includes Role[:author].permissions, Permission[:posts_edit]
28
+ assert_includes Role[:editor].permissions, Permission[:"posts#edit"]
29
+ end
30
+
31
+ test "roles inherit permissions from parent roles" do
32
+ assert_includes Role[:editor].permissions, Permission[:posts]
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class UserPermissionTest < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class UserRoleTest < ActiveSupport::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ end
@@ -0,0 +1,77 @@
1
+ require 'test_helper'
2
+
3
+ module ExpressAccess
4
+ class UserTest < ActiveSupport::TestCase
5
+
6
+ setup do
7
+ ExpressAccess.initialize_user!
8
+ end
9
+
10
+ test "ExpressAccess::User included in ::User" do
11
+ assert_includes ::User.ancestors, ExpressAccess::User
12
+ end
13
+
14
+ test "user has direct permissions" do
15
+ assert_includes users(:editor).direct_permissions, Permission[:something_special]
16
+ end
17
+
18
+ test "user permissions include direct_permissions" do
19
+ assert users(:editor).direct_permissions.to_set <= users(:editor).permissions
20
+ end
21
+
22
+ test "#may? is true if a user has that permission" do
23
+ assert users(:editor).may?(:something_special)
24
+ assert users(:editor).may_do?(:something_special)
25
+ end
26
+
27
+ test "user has roles" do
28
+ assert users(:editor).roles.includes(Role[:editor])
29
+ end
30
+
31
+ test "user has permissions through role" do
32
+ assert users(:editor).may_do?("posts#edit")
33
+ end
34
+
35
+ test "user permissions includes role and direct permissions" do
36
+ assert users(:editor).direct_permissions.to_set <= users(:editor).permissions
37
+ assert users(:editor).role_permissions.to_set <= users(:editor).permissions
38
+ end
39
+
40
+ test "user provides sexy method_missing dsl" do
41
+ assert users(:editor).may_edit?(:posts)
42
+ end
43
+
44
+ # that which is not forbidden is permitted
45
+ test "user may do something if no such permission exists" do
46
+ assert users(:editor).may_destroy?(:posts)
47
+ end
48
+
49
+ test "user may not do something if they do not have permission" do
50
+ assert_not users(:nobody).may_edit?(:posts)
51
+ end
52
+
53
+ test "user can be assigned direct_permissions" do
54
+ refute users(:author).may_do?(:something_special)
55
+ users(:author).direct_permission_ids = [Permission[:something_special].id]
56
+ assert users(:author).may_do?(:something_special)
57
+ end
58
+
59
+ test "#after_sign_in_path retrieves after_sign_in_path from roles" do
60
+ author = users(:author)
61
+
62
+ assert_equal "/posts", author.after_sign_in_path
63
+ end
64
+
65
+ test "#after_sign_in_path returns nil if any of the user's roles don't have #after_sign_in_path value" do
66
+ editor = users(:editor)
67
+
68
+ assert_nil editor.after_sign_in_path
69
+ end
70
+
71
+ test "#after_sign_in_path returns nil if user has no roles" do
72
+ nobody = users(:nobody)
73
+
74
+ assert_nil nobody.after_sign_in_path
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,19 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
+ require "rails/test_help"
6
+ require 'minitest/rg'
7
+ require 'minitest/mock'
8
+ require 'pry'
9
+
10
+ Rails.backtrace_cleaner.remove_silencers!
11
+
12
+ # Load support files
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
14
+
15
+
16
+ if ActiveSupport::TestCase.respond_to?(:fixture_path=)
17
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
18
+ ActiveSupport::TestCase.fixtures :all
19
+ end