releaf-permissions 0.2.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -21
  3. data/app/assets/stylesheets/{releaf/controllers → controllers}/releaf/permissions/sessions.scss +0 -0
  4. data/app/builders/releaf/permissions/page/header_builder.rb +35 -0
  5. data/app/builders/releaf/permissions/page/layout_builder.rb +17 -0
  6. data/app/builders/releaf/permissions/page/menu_builder.rb +18 -0
  7. data/app/builders/releaf/permissions/roles/form_builder.rb +10 -6
  8. data/app/builders/releaf/permissions/roles/table_builder.rb +2 -6
  9. data/app/controllers/releaf/permissions/profile_controller.rb +25 -46
  10. data/app/controllers/releaf/permissions/roles_controller.rb +3 -5
  11. data/app/controllers/releaf/permissions/sessions_controller.rb +17 -27
  12. data/app/controllers/releaf/permissions/users_controller.rb +11 -14
  13. data/app/models/releaf/permissions/role.rb +0 -25
  14. data/app/models/releaf/permissions/user.rb +3 -9
  15. data/lib/releaf-permissions.rb +23 -27
  16. data/lib/releaf/permissions/access_control.rb +37 -0
  17. data/lib/releaf/permissions/configuration.rb +26 -0
  18. data/lib/releaf/permissions/controller_support.rb +33 -0
  19. data/lib/releaf/permissions/default_controller_resolver.rb +22 -0
  20. data/lib/releaf/permissions/engine.rb +1 -18
  21. data/lib/releaf/permissions/layout.rb +5 -0
  22. data/lib/releaf/permissions/{profile_component.rb → profile.rb} +6 -2
  23. data/lib/releaf/permissions/{roles_component.rb → roles.rb} +2 -2
  24. data/lib/releaf/permissions/settings_manager.rb +22 -0
  25. data/lib/releaf/permissions/users.rb +11 -0
  26. data/spec/builders/releaf/permissions/page/header_builder_spec.rb +87 -0
  27. data/spec/builders/releaf/permissions/page/layout_builder_spec.rb +64 -0
  28. data/spec/builders/releaf/permissions/page/menu_builder_spec.rb +100 -0
  29. data/spec/builders/{profile → releaf/permissions/profile}/form_builder_spec.rb +0 -0
  30. data/spec/builders/releaf/permissions/roles/form_builder_spec.rb +56 -0
  31. data/spec/builders/releaf/permissions/roles/table_builder_spec.rb +41 -0
  32. data/spec/builders/{users → releaf/permissions/users}/form_builder_spec.rb +0 -0
  33. data/spec/builders/{users → releaf/permissions/users}/table_builder_spec.rb +0 -0
  34. data/spec/controllers/permissions/profile_controller_spec.rb +0 -27
  35. data/spec/controllers/permissions/users_controller_spec.rb +1 -1
  36. data/spec/features/roles_spec.rb +3 -3
  37. data/spec/features/users_spec.rb +2 -2
  38. data/spec/lib/access_control_spec.rb +35 -50
  39. data/spec/lib/releaf/permissions/configuration_spec.rb +38 -0
  40. data/spec/lib/releaf/permissions/controller_support_spec.rb +76 -0
  41. data/spec/lib/releaf/permissions/default_controller_resolver_spec.rb +49 -0
  42. data/spec/lib/releaf/permissions/layout_spec.rb +10 -0
  43. data/spec/lib/releaf/permissions/profile_spec.rb +11 -0
  44. data/spec/lib/releaf/permissions/roles_spec.rb +10 -0
  45. data/spec/lib/releaf/permissions/settings_manager_spec.rb +38 -0
  46. data/spec/lib/releaf/permissions/users_spec.rb +17 -0
  47. data/spec/models/permissions/role_spec.rb +0 -28
  48. data/spec/models/permissions/user_spec.rb +33 -3
  49. metadata +56 -32
  50. data/app/controllers/releaf/permissions/home_controller.rb +0 -32
  51. data/app/lib/releaf/permissions/access_control.rb +0 -36
  52. data/lib/releaf/permissions/builders_autoload.rb +0 -11
  53. data/lib/releaf/permissions/devise_component.rb +0 -8
  54. data/lib/releaf/permissions/users_component.rb +0 -7
  55. data/releaf-permissions.gemspec +0 -19
  56. data/spec/builders/roles/form_builder_spec.rb +0 -38
  57. data/spec/builders/roles/table_builder_spec.rb +0 -29
  58. data/spec/controllers/permissions/home_controller_spec.rb +0 -52
@@ -0,0 +1,37 @@
1
+ module Releaf::Permissions
2
+ class AccessControl
3
+ include Virtus.model(strict: true)
4
+ attribute :user, Object
5
+
6
+ def self.initialize_component
7
+ ActiveSupport.on_load :base_controller do
8
+ Releaf::ActionController.send(:include, Releaf::Permissions::ControllerSupport)
9
+ end
10
+ end
11
+
12
+ def self.draw_component_routes(router)
13
+ router.devise_for(Releaf.application.config.permissions.devise_for, path: "", controllers: { sessions: "releaf/permissions/sessions" })
14
+ end
15
+
16
+ def controller_permitted?(controller_name)
17
+ allowed_controllers.include?(controller_name)
18
+ end
19
+
20
+ def allowed_controllers
21
+ permanent_allowed_controllers + role_allowed_controllers
22
+ end
23
+
24
+ def role_allowed_controllers
25
+ user.role.permissions.map{|permission| controller_name_from_permission(permission.permission) }.compact
26
+ end
27
+
28
+ def controller_name_from_permission(permission)
29
+ match = permission.match(/^controller\.(.+)/)
30
+ match[1] if match
31
+ end
32
+
33
+ def permanent_allowed_controllers
34
+ Releaf.application.config.permissions.permanent_allowed_controllers
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Releaf::Permissions
2
+ class Configuration
3
+ include Virtus.model(strict: true)
4
+ attribute :devise_for, String
5
+ attribute :access_control, Object
6
+ attribute :permanent_allowed_controllers, Array
7
+
8
+ def devise_model_name
9
+ devise_for.tr("/", "_")
10
+ end
11
+
12
+ def devise_model_class
13
+ devise_for.classify.constantize
14
+ end
15
+
16
+ def self.configure_component
17
+ Releaf.application.config.add_configuration(
18
+ new(
19
+ devise_for: "releaf/permissions/user",
20
+ access_control: Releaf::Permissions::AccessControl,
21
+ permanent_allowed_controllers: ['releaf/root', 'releaf/errors']
22
+ )
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ module Releaf::Permissions
2
+ module ControllerSupport
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend_before_action :set_locale, :verify_controller_access!, :authenticate!
7
+ end
8
+
9
+ def set_locale
10
+ I18n.locale = user.locale
11
+ end
12
+
13
+ def verify_controller_access!
14
+ unless Releaf.application.config.permissions.access_control.new(user: user).controller_permitted?(short_name)
15
+ raise Releaf::AccessDenied
16
+ end
17
+ end
18
+
19
+ def user
20
+ send("current_#{Releaf.application.config.permissions.devise_model_name}")
21
+ end
22
+
23
+ def authorized?
24
+ method_name = "#{Releaf.application.config.permissions.devise_model_name}_signed_in?"
25
+ send(method_name)
26
+ end
27
+
28
+ def authenticate!
29
+ method_name = "authenticate_#{Releaf.application.config.permissions.devise_model_name}!"
30
+ send(method_name)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ module Releaf::Permissions
2
+ class DefaultControllerResolver < Releaf::Root::DefaultControllerResolver
3
+
4
+ def self.configure_component
5
+ Releaf.application.config.root.default_controller_resolver = self
6
+ end
7
+
8
+ def controllers
9
+ # Note: This basically sorts allowed controllers in order specified by
10
+ # Releaf.application.config.available_controllers
11
+ ([user.role.default_controller] + super).uniq & allowed_controllers
12
+ end
13
+
14
+ def allowed_controllers
15
+ Releaf.application.config.permissions.access_control.new(user: user).allowed_controllers
16
+ end
17
+
18
+ def user
19
+ current_controller.user
20
+ end
21
+ end
22
+ end
@@ -1,24 +1,7 @@
1
- require 'devise'
2
-
3
1
  module Releaf::Permissions
4
- require 'releaf/permissions/devise_component'
5
- require 'releaf/permissions/profile_component'
6
- require 'releaf/permissions/roles_component'
7
- require 'releaf/permissions/users_component'
8
- require 'releaf/permissions/builders_autoload'
9
-
10
2
  class Engine < ::Rails::Engine
11
3
  initializer 'precompile', group: :all do |app|
12
- app.config.assets.precompile += %w(releaf/controllers/releaf/permissions/*)
4
+ app.config.assets.precompile += %w(controllers/releaf/permissions/*)
13
5
  end
14
6
  end
15
-
16
- def self.components
17
- [
18
- Releaf::Permissions::DeviseComponent,
19
- Releaf::Permissions::RolesComponent,
20
- Releaf::Permissions::UsersComponent,
21
- Releaf::Permissions::ProfileComponent
22
- ]
23
- end
24
7
  end
@@ -0,0 +1,5 @@
1
+ module Releaf::Permissions::Layout
2
+ def self.configure_component
3
+ Releaf.application.config.layout_builder_class_name = 'Releaf::Permissions::Page::LayoutBuilder'
4
+ end
5
+ end
@@ -1,9 +1,13 @@
1
- module Releaf::Permissions::ProfileComponent
1
+ module Releaf::Permissions::Profile
2
+
3
+ def self.configure_component
4
+ Releaf.application.config.additional_controllers = Releaf.application.config.additional_controllers + ['releaf/permissions/profile']
5
+ end
6
+
2
7
  def self.draw_component_routes router
3
8
  router.namespace :releaf, path: nil do
4
9
  router.get "profile", to: "permissions/profile#edit", as: :permissions_user_profile
5
10
  router.patch "profile", to: "permissions/profile#update"
6
- router.post "profile/settings", to: "permissions/profile#settings", as: :permissions_user_profile_settings
7
11
  end
8
12
  end
9
13
  end
@@ -1,5 +1,5 @@
1
- module Releaf::Permissions::RolesComponent
2
- extend Releaf::Core::Component
1
+ module Releaf::Permissions::Roles
2
+ extend Releaf::Component
3
3
 
4
4
  def self.draw_component_routes router
5
5
  resource_route(router, :permissions, :roles)
@@ -0,0 +1,22 @@
1
+ module Releaf::Permissions
2
+ class SettingsManager
3
+ def self.configure_component
4
+ Releaf.application.config.settings_manager = self
5
+ end
6
+
7
+ def self.read(controller:, key:)
8
+ controller.user.settings[key] if controller.respond_to? :user
9
+ end
10
+
11
+ def self.write(controller:, key:, value:)
12
+ # Sometimes concurrency happens, so lets try until
13
+ # record get updated
14
+ begin
15
+ controller.user.settings[key] = value
16
+ rescue ActiveRecord::RecordNotUnique
17
+ retry
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,11 @@
1
+ module Releaf::Permissions::Users
2
+ extend Releaf::Component
3
+
4
+ def self.configure_component
5
+ Releaf.application.config.permissions.devise_for = 'releaf/permissions/user'
6
+ end
7
+
8
+ def self.draw_component_routes(router)
9
+ resource_route(router, :permissions, :users)
10
+ end
11
+ end
@@ -0,0 +1,87 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Page::HeaderBuilder, type: :class do
4
+ class PageHeaderBuilderTestHelper < ActionView::Base
5
+ include Rails.application.routes.url_helpers
6
+ include FontAwesome::Rails::IconHelper
7
+
8
+ def protect_against_forgery?
9
+ true
10
+ end
11
+
12
+ def form_authenticity_token
13
+ "xxx"
14
+ end
15
+
16
+ def request_forgery_protection_token
17
+ "yyy"
18
+ end
19
+ end
20
+
21
+ subject { described_class.new(template) }
22
+ let(:template){ PageHeaderBuilderTestHelper.new }
23
+
24
+ describe "#items" do
25
+ it "returns array of home link, profile block and logout form content" do
26
+ allow(subject).to receive(:home_link).and_return("a")
27
+ allow(subject).to receive(:profile_block).and_return("b")
28
+ allow(subject).to receive(:sign_out_form).and_return("c")
29
+ expect(subject.items).to eq(["a", "b", "c"])
30
+ end
31
+ end
32
+
33
+ describe "#profile_path" do
34
+ it "returns profile edit url for defined profile controller" do
35
+ expect(subject.profile_path).to eq("/admin/profile")
36
+ end
37
+ end
38
+
39
+ describe "#profile_block" do
40
+ it "returns profile block with content" do
41
+ allow(subject).to receive(:profile_user_name).and_return("neim")
42
+ allow(subject).to receive(:profile_path).and_return("url_b")
43
+ content = '<a class="button profile" href="url_b"><span class="name">neim</span></a>'
44
+ expect(subject.profile_block).to eq(content)
45
+ end
46
+ end
47
+
48
+ describe "#user" do
49
+ it "returns permissions manager user" do
50
+ controller = Releaf::RootController.new
51
+ allow(subject).to receive(:controller).and_return(controller)
52
+ allow(controller).to receive(:user).and_return("x")
53
+ expect(subject.user).to eq("x")
54
+ end
55
+ end
56
+
57
+ describe "#profile_user_name" do
58
+ it "returns title for user instance" do
59
+ user = Releaf::Permissions::User.new(name: "a", surname: "b")
60
+ allow(subject).to receive(:user).and_return(user)
61
+ allow(subject).to receive(:resource_title).with(user).and_return("x t")
62
+ expect(subject.profile_user_name).to eq("x t")
63
+ end
64
+ end
65
+
66
+ describe "#sign_out_path" do
67
+ it "returns sign out url" do
68
+ expect(subject.sign_out_path).to eq("/admin/sign_out")
69
+ end
70
+ end
71
+
72
+ describe "#sign_out_form" do
73
+ it "returns sign out form" do
74
+ allow(subject).to receive(:sign_out_path).and_return("url_a")
75
+ content = %Q[
76
+ <form class="sign-out" action="url_a" accept-charset="UTF-8" method="post">
77
+ <input name="utf8" type="hidden" value="&#x2713;" />
78
+ <input type="hidden" name="_method" value="delete" />
79
+ <input type="hidden" name="yyy" value="xxx" />
80
+ <button class="button only-icon" type="submit" title="Sign out">
81
+ <i class="fa fa-power-off fa-icon-header"></i>
82
+ </button>
83
+ </form>]
84
+ expect(subject.sign_out_form).to match_html( content )
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,64 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Page::LayoutBuilder, type: :class do
4
+ class PermissionsLayoutBuilderView < ActionView::Base; end
5
+ let(:controller){ Releaf::RootController.new }
6
+ let(:template){ PermissionsLayoutBuilderView.new }
7
+ subject { described_class.new(template) }
8
+
9
+ before do
10
+ allow(subject).to receive(:controller).and_return(controller)
11
+ end
12
+
13
+ it "inherits Releaf::Builders::Page::LayoutBuilder" do
14
+ expect(described_class.superclass).to eq(Releaf::Builders::Page::LayoutBuilder)
15
+ end
16
+
17
+ describe "#header_builder" do
18
+ it "returns `Releaf::Permissions::Page::HeaderBuilder` class" do
19
+ expect(subject.header_builder).to eq(Releaf::Permissions::Page::HeaderBuilder)
20
+ end
21
+ end
22
+
23
+ describe "#menu_builder" do
24
+ it "returns `Releaf::Permissions::Page::MenuBuilder` class" do
25
+ expect(subject.menu_builder).to eq(Releaf::Permissions::Page::MenuBuilder)
26
+ end
27
+ end
28
+
29
+ describe "#body_content" do
30
+ before do
31
+ allow(subject).to receive(:header).and_return("_header")
32
+ allow(subject).to receive(:menu).and_return("_menu")
33
+ allow(subject).to receive(:notifications).and_return("_notifications")
34
+ end
35
+
36
+ context "when controller responds to `authorized?` and `authorized?` call return true" do
37
+ it "returns `super` content" do
38
+ allow(controller).to receive(:authorized?).and_return(true)
39
+ expect(subject.body_content{ "x" }).to eq("_header_menu<main id=\"main\">x</main>_notifications")
40
+ end
41
+ end
42
+
43
+ context "when controller responds to `authorized?` and `authorized?` call return false" do
44
+ it "returns given block content" do
45
+ allow(controller).to receive(:authorized?).and_return(false)
46
+ expect(subject.body_content{ "x" }).to eq("x")
47
+ end
48
+ end
49
+
50
+ context "when controller does not responds to `authorized?`" do
51
+ it "returns given block content" do
52
+ allow(controller).to receive(:respond_to?).with(:authorized?).and_return(false)
53
+ expect(subject.body_content{ "x" }).to eq("x")
54
+ end
55
+ end
56
+ end
57
+ #def body_content(&block)
58
+ #if controller.respond_to?(:authorized?) && controller.authorized?
59
+ #super
60
+ #else
61
+ #yield
62
+ #end
63
+ #end
64
+ end
@@ -0,0 +1,100 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Permissions::Page::MenuBuilder, type: :class do
4
+ class MenuBuilderTestHelper < ActionView::Base
5
+ include FontAwesome::Rails::IconHelper
6
+ end
7
+
8
+ let(:controller){ Releaf::ActionController.new }
9
+ let(:template){ MenuBuilderTestHelper.new }
10
+ let(:group_item){ Releaf::ControllerGroupDefinition.new(name: "x", items: []) }
11
+ let(:controller_item){ Releaf::ControllerDefinition.new(name: "y", controller: "_controller_") }
12
+ subject { described_class.new(template) }
13
+
14
+ before do
15
+ allow(template).to receive(:controller).and_return(controller)
16
+ end
17
+
18
+ it "inherits `Releaf::Builders::Page::MenuBuilder`" do
19
+ expect(described_class.ancestors).to include(Releaf::Builders::Page::MenuBuilder)
20
+ end
21
+
22
+ describe "#menu_item" do
23
+ before do
24
+ allow(subject).to receive(:item_attributes).and_return({})
25
+ allow(subject).to receive(:menu_item_group).and_return("_content_")
26
+ end
27
+
28
+ context "when item is permitted" do
29
+ it "returns parent method content" do
30
+ allow(subject).to receive(:menu_item_permitted?).with(group_item).and_return(true)
31
+ expect(subject.menu_item(group_item)).to eq("<li>_content_</li>")
32
+ end
33
+ end
34
+
35
+ context "when item is not permitted" do
36
+ it "returns nil" do
37
+ allow(subject).to receive(:menu_item_permitted?).with(group_item).and_return(false)
38
+ expect(subject.menu_item(group_item)).to be nil
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#menu_item_permitted?" do
44
+ context "when item is instance of `Releaf::ControllerGroupDefinition`" do
45
+ before do
46
+ allow(group_item).to receive(:controllers).and_return([
47
+ Releaf::ControllerDefinition.new(name: "a1", controller: "c1"),
48
+ Releaf::ControllerDefinition.new(name: "a2", controller: "c2"),
49
+ Releaf::ControllerDefinition.new(name: "a3", controller: "c3"),
50
+ ])
51
+ end
52
+
53
+ context "when any of group item controller is allowed" do
54
+ it "returns true" do
55
+ allow(subject).to receive(:controller_permitted?).with("c1").and_return(false)
56
+ allow(subject).to receive(:controller_permitted?).with("c2").and_return(true)
57
+ expect(subject).to_not receive(:controller_permitted?).with("c3")
58
+ expect(subject.menu_item_permitted?(group_item)).to be true
59
+ end
60
+ end
61
+
62
+ context "when none of group item controller is allowed" do
63
+ it "returns false" do
64
+ allow(subject).to receive(:controller_permitted?).with("c1").and_return(false)
65
+ allow(subject).to receive(:controller_permitted?).with("c2").and_return(false)
66
+ allow(subject).to receive(:controller_permitted?).with("c3").and_return(false)
67
+ expect(subject.menu_item_permitted?(group_item)).to be false
68
+ end
69
+ end
70
+ end
71
+
72
+ context "when item is instance of `Releaf::ControllerDefinition`" do
73
+ context "when item controller is allowed" do
74
+ it "returns true" do
75
+ allow(subject).to receive(:controller_permitted?).with("_controller_").and_return(true)
76
+ expect(subject.menu_item_permitted?(controller_item)).to be true
77
+ end
78
+ end
79
+
80
+ context "when item controller is not allowed" do
81
+ it "returns false" do
82
+ allow(subject).to receive(:controller_permitted?).with("_controller_").and_return(false)
83
+ expect(subject.menu_item_permitted?(controller_item)).to be false
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "#controller_permitted?" do
90
+ it "returns access controller controller permission query result for given controller name" do
91
+ user = Releaf::Permissions::User.new
92
+ allow(controller).to receive(:user).and_return("x")
93
+ access_control = Releaf::Permissions::AccessControl.new(user: user)
94
+ allow(Releaf.application.config.permissions.access_control).to receive(:new).with(user: "x").and_return(access_control)
95
+ allow(access_control).to receive(:controller_permitted?).with("kjasdasd").and_return("_true")
96
+
97
+ expect(subject.controller_permitted?("kjasdasd")).to eq("_true")
98
+ end
99
+ end
100
+ end