releaf-permissions 0.2.1 → 1.0.3

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.
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