lesli 5.0.3 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/lesli_manifest.js +2 -1
  3. data/app/assets/images/lesli/brand/app-logo2.svg +52 -0
  4. data/app/assets/javascripts/lesli/users/confirmations.js +32 -0
  5. data/app/assets/javascripts/lesli/users/passwords.js +3 -3
  6. data/app/assets/javascripts/lesli/users/registrations.js +3 -3
  7. data/app/assets/javascripts/lesli/users/sessions.js +3 -3
  8. data/app/assets/stylesheets/lesli/users/confirmations.scss +28 -6
  9. data/app/controllers/lesli/application_controller.rb +3 -3
  10. data/app/controllers/lesli/application_lesli_controller.rb +2 -2
  11. data/app/controllers/lesli/interfaces/application/authorization.rb +2 -2
  12. data/app/controllers/lesli/interfaces/application/logger.rb +14 -38
  13. data/app/controllers/lesli/interfaces/application/requester.rb +1 -1
  14. data/app/controllers/lesli/roles_controller.rb +3 -1
  15. data/app/controllers/lesli/shared/dashboards_controller.rb +308 -0
  16. data/app/controllers/users/confirmations_controller.rb +63 -27
  17. data/app/controllers/users/passwords_controller.rb +67 -30
  18. data/app/controllers/users/sessions_controller.rb +2 -4
  19. data/app/helpers/lesli/general_helper.rb +1 -1
  20. data/app/helpers/lesli/navigation_helper.rb +17 -16
  21. data/app/lib/lesli/system.rb +14 -5
  22. data/app/mailers/lesli/application_lesli_mailer.rb +8 -19
  23. data/app/mailers/lesli/devise_mailer.rb +29 -3
  24. data/app/models/concerns/account_initializer.rb +100 -0
  25. data/app/models/concerns/{user_guard.rb → user_security.rb} +7 -8
  26. data/app/models/lesli/account.rb +9 -26
  27. data/app/models/lesli/application_lesli_record.rb +2 -1
  28. data/app/models/lesli/descriptor/privilege.rb +38 -0
  29. data/app/models/lesli/descriptor.rb +18 -1
  30. data/app/models/lesli/role/power.rb +70 -0
  31. data/app/models/lesli/role/privilege.rb +38 -0
  32. data/app/models/lesli/role.rb +20 -15
  33. data/app/models/lesli/shared/dashboard.rb +162 -0
  34. data/app/models/lesli/system_controller.rb +1 -0
  35. data/app/models/lesli/user/{role.rb → power.rb} +1 -1
  36. data/app/{services/lesli/role_service.rb → models/lesli/user/setting.rb} +10 -9
  37. data/app/models/lesli/user.rb +11 -20
  38. data/app/operators/lesli/controller_operator.rb +148 -0
  39. data/app/operators/lesli/descriptor_privilege_operator.rb +75 -0
  40. data/app/operators/lesli/role_power_operator.rb +108 -0
  41. data/app/operators/lesli/user_registration_operator.rb +121 -0
  42. data/app/services/lesli/user_service.rb +2 -4
  43. data/app/services/lesli/{user/session_service.rb → user_session_service.rb} +11 -4
  44. data/app/views/devise/confirmations/new.html.erb +0 -14
  45. data/app/views/devise/confirmations/show.html.erb +63 -0
  46. data/app/views/devise/passwords/edit.html.erb +78 -24
  47. data/app/views/devise/passwords/new.html.erb +2 -3
  48. data/app/views/lesli/emails/devise_mailer/confirmation_instructions.html.erb +1 -1
  49. data/app/views/lesli/emails/devise_mailer/reset_password_instructions.html.erb +23 -0
  50. data/app/views/lesli/partials/_application-lesli-engines.html.erb +1 -1
  51. data/app/views/lesli/partials/_application-lesli-header.html.erb +3 -1
  52. data/app/views/lesli/partials/_application-lesli-icons.html.erb +1 -1
  53. data/config/initializers/devise.rb +2 -0
  54. data/config/locales/translations.en.yml +13 -3
  55. data/config/locales/translations.es.yml +13 -3
  56. data/config/routes.rb +4 -2
  57. data/db/migrate/v1.0/0010000110_create_lesli_accounts.rb +2 -0
  58. data/db/{tables/0010001010_create_account_settings.rb → migrate/v1.0/0010001010_create_lesli_account_settings.rb} +5 -5
  59. data/db/{tables/0010003110_create_user_settings.rb → migrate/v1.0/0010003110_create_lesli_user_settings.rb} +4 -4
  60. data/db/migrate/v1.0/0010003210_create_lesli_user_sessions.rb +6 -2
  61. data/db/migrate/v1.0/{0010003410_create_lesli_user_roles.rb → 0010003410_create_lesli_user_powers.rb} +4 -4
  62. data/db/migrate/v1.0/0010005010_create_lesli_descriptors.rb +1 -1
  63. data/db/migrate/v1.0/{0010003910_create_lesli_user_agents.rb → 0010005510_create_lesli_role_powers.rb} +7 -9
  64. data/db/{tables/0010005710_create_role_privileges.rb → migrate/v1.0/0010005710_create_lesli_role_privileges.rb} +6 -6
  65. data/db/seed/development/users.rb +3 -4
  66. data/db/seed/tools.rb +4 -4
  67. data/db/seeds.rb +16 -29
  68. data/lib/lesli/engine.rb +33 -10
  69. data/lib/lesli/version.rb +1 -1
  70. data/lib/mailer_previews/devise_mailer_preview.rb +7 -0
  71. data/lib/sass/lesli/bulma/loader.scss +3 -0
  72. data/lib/sass/lesli/layouts/application-navbar.scss +1 -1
  73. data/lib/sass/lesli/pages/devise-simple.scss +2 -1
  74. data/lib/tasks/lesli/controllers.rake +1 -94
  75. data/lib/tasks/lesli/db.rake +43 -3
  76. data/lib/tasks/lesli/dev.rake +66 -0
  77. data/lib/tasks/lesli/engine.rake +59 -0
  78. data/lib/tasks/lesli/privileges.rake +54 -0
  79. data/lib/tasks/lesli_tasks.rake +5 -0
  80. data/lib/vue/application.js +11 -3
  81. data/lib/vue/devise/passwords.js +7 -7
  82. data/lib/vue/devise/registrations.js +2 -2
  83. data/lib/vue/devise/sessions.js +11 -6
  84. data/lib/vue/layouts/application-header.vue +15 -3
  85. data/lib/vue/shared/dashboards/apps/edit.vue +215 -0
  86. data/lib/vue/{apps → shared}/dashboards/apps/index.vue +3 -5
  87. data/lib/vue/{apps → shared}/dashboards/apps/show.vue +26 -16
  88. data/lib/vue/{apps → shared}/dashboards/components/form.vue +31 -43
  89. data/lib/vue/shared/stores/dashboard.js +251 -0
  90. data/lib/vue/stores/translations.json +24 -72
  91. data/lib/vue/stores/{user.js → users.js} +1 -1
  92. data/lib/webpack/base.js +3 -2
  93. data/lib/webpack/core.js +2 -1
  94. data/readme.md +23 -7
  95. metadata +63 -65
  96. data/app/models/concerns/account_engines.rb +0 -249
  97. data/app/models/concerns/user_polyfill.rb +0 -134
  98. data/db/migrate/v1.0/0010001510_create_lesli_account_requests.rb +0 -45
  99. data/db/migrate/v1.0/0010003810_create_lesli_user_requests.rb +0 -44
  100. data/db/tables/0010005510_create_role_descriptors.rb +0 -44
  101. data/lib/vue/apps/dashboards/apps/edit.vue +0 -105
  102. data/lib/vue/apps/dashboards/components/preview.vue +0 -172
  103. /data/app/assets/icons/lesli/{cloud-vault.svg → cloud-guard.svg} +0 -0
  104. /data/lib/vue/{apps → shared}/cloudobjects/action.vue +0 -0
  105. /data/lib/vue/{apps → shared}/cloudobjects/discussion/content.vue +0 -0
  106. /data/lib/vue/{apps → shared}/cloudobjects/discussion/element.vue +0 -0
  107. /data/lib/vue/{apps → shared}/cloudobjects/discussion/filters.vue +0 -0
  108. /data/lib/vue/{apps → shared}/cloudobjects/discussion/new.vue +0 -0
  109. /data/lib/vue/{apps → shared}/cloudobjects/discussion.vue +0 -0
  110. /data/lib/vue/{apps → shared}/cloudobjects/file/grid.vue +0 -0
  111. /data/lib/vue/{apps → shared}/cloudobjects/file/list.vue +0 -0
  112. /data/lib/vue/{apps → shared}/cloudobjects/file.vue +0 -0
  113. /data/lib/vue/{apps → shared}/dashboards/apps/new.vue +0 -0
  114. /data/lib/vue/{apps → shared}/workflows2/apps/actions/form.vue +0 -0
  115. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/chatroom-form.vue +0 -0
  116. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/cloud-object-clone-form.vue +0 -0
  117. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/cloud-object-file-form.vue +0 -0
  118. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/email-form.vue +0 -0
  119. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/notification-form.vue +0 -0
  120. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/send-cloud-object-file.vue +0 -0
  121. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/task-form.vue +0 -0
  122. /data/lib/vue/{apps → shared}/workflows2/apps/actions/index.vue +0 -0
  123. /data/lib/vue/{apps → shared}/workflows2/apps/checks/form.vue +0 -0
  124. /data/lib/vue/{apps → shared}/workflows2/apps/checks/index.vue +0 -0
  125. /data/lib/vue/{apps → shared}/workflows2/apps/index.vue +0 -0
  126. /data/lib/vue/{apps → shared}/workflows2/apps/new.vue +0 -0
  127. /data/lib/vue/{apps → shared}/workflows2/apps/show.vue +0 -0
  128. /data/lib/vue/{apps → shared}/workflows2/components/associations.vue +0 -0
  129. /data/lib/vue/{apps → shared}/workflows2/components/chart.vue +0 -0
  130. /data/lib/vue/{apps → shared}/workflows2/components/workflow-form.vue +0 -0
  131. /data/lib/vue/{apps → shared}/workflows2/components/workflow-status-dropdown.vue +0 -0
@@ -0,0 +1,162 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Your Smart Business Assistant.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+
32
+ =end
33
+
34
+ module Lesli
35
+ module Shared
36
+ class Dashboard < ::Lesli::ApplicationLesliRecord
37
+ self.abstract_class = true
38
+
39
+ belongs_to :user_creator, class_name: "User", foreign_key: "users_id", optional: true
40
+
41
+ after_update :verify_default_dashboard
42
+ after_create :verify_default_dashboard
43
+
44
+ enum component_ids: {}
45
+
46
+ # @return [void]
47
+ # @param account [LesliEngine::Account]
48
+ # @description Initializes a default dummy dashboard for the account. This guarantees that the users
49
+ # will be able to access the dashboard page. Even if it's empty.
50
+ # @example
51
+ # # Imagine you are adding a new engine to your instance (CloudProposal)
52
+ # # To execute this function, you must only do
53
+ # my_account = Account.first
54
+ # my_account.proposal = CloudProposal::Account.new
55
+ def self.initialize_data(account)
56
+ self.create!(
57
+ account: account,
58
+ name: "Default Dashboard",
59
+ default: true,
60
+ main: false
61
+ )
62
+ end
63
+
64
+ # @return [Hash] Hash of containing the information of the dashboard and its components.
65
+ # @description Returns a hash with information about the dashboard and all its *components*.
66
+ # Each component is returned in the configuration view, not the render view. This means that
67
+ # this method is ment to be used when updating the dashboard
68
+ # @example
69
+ # respond_with_successful(CloudHelp::Dashboard.first.show)
70
+ def show
71
+ attributes.merge({
72
+ components: components.order(index: :asc)
73
+ # components: [{
74
+ # name: "ticket",
75
+ # component_id: "ticket"
76
+ # },{
77
+ # name: "ticket",
78
+ # component_id: "ticket"
79
+ # }]
80
+ })
81
+ end
82
+
83
+ # @return [Hash] Hash containing the options to create and manage dashboards
84
+ # @param current_user [User] The user that made this request
85
+ # @param query [Hash] Query containing filters. Currently unused, but required
86
+ # @descriptions Returns a list of options needed to create and manage dashboard components. For now,
87
+ # the returned options are: A list of roles, the enoun containing the component_ids, generic configuration options
88
+ # for all dashboad components and a descriptions hash, that contains brief descriptions for each component to help
89
+ # the user understand what each component does. Any class that inherits from this one can send a block to add extra
90
+ # functionality. For example, the descriptions must be implemented directly from the engine.
91
+ # @example
92
+ # CloudHouse::Dashboard.options(User.find(2), nil)
93
+ def self.options(current_user, query)
94
+ dynamic_info = self.dynamic_info
95
+ component_model = dynamic_info[:module_model_component]
96
+
97
+ component_ids = component_model.component_ids.map do |comp|
98
+ {
99
+ value: comp,
100
+ text: comp
101
+ }
102
+ end
103
+
104
+ options = {
105
+ component_ids: component_ids,
106
+ #components_configuration_options: component_model.configuration_options,
107
+ descriptions: {}
108
+ }
109
+
110
+ if block_given?
111
+ yield(options)
112
+ else
113
+ return options
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # @return [void]
120
+ # @descriptions This is an after_updated and after_create method that validates that at any moment in time, there is only
121
+ # one default dashboard in the engine. If there is another default dashboard, it's *default* field is set to false
122
+ # before committing the changes
123
+ # @example
124
+ # CloudFocus::Dasbhoard.where(default: false).first.update!(default: true)
125
+ # # This will automatically trigger this function and remove the *default* field from the old default dashboard
126
+ def verify_default_dashboard
127
+ if default
128
+ dashboards = self.class.where.not(id: id).where(account: account)
129
+ self.class.where.not(id: id).where(account: account).update_all(default: false)
130
+ end
131
+
132
+ unless self.class.where(account: account).find_by(default: true)
133
+ errors.add(:base, I18n.t("core.dashboards.messages_danger_default_dashboard_must_exist"))
134
+ raise ActiveRecord::Rollback
135
+ end
136
+ end
137
+
138
+ # Build the Rails models and engine information for
139
+ # the current engine implementing the shared dashboards
140
+ # Example: For the LesliAudit engine
141
+ # {
142
+ # :module_name => "audit",
143
+ # :module_name_full => "LesliAudit",
144
+ # :module_model => "LesliAudit::Dashboard",
145
+ # :module_model_component => "LesliAudit::Dashboard::Component"
146
+ # }
147
+ def self.dynamic_info
148
+
149
+ module_info = self.name.split("::")
150
+
151
+ module_name = module_info[0].sub("Lesli", "").downcase
152
+
153
+ {
154
+ module_name: module_name,
155
+ module_name_full: module_info[0],
156
+ module_model: "#{ module_info[0] }::Dashboard".constantize,
157
+ module_model_component: "#{ module_info[0] }::Dashboard::Component".constantize
158
+ }
159
+ end
160
+ end
161
+ end
162
+ end
@@ -85,6 +85,7 @@ module Lesli
85
85
  cc[engine][controller] = {
86
86
  id: c[:controller_id],
87
87
  name: c[:controller_name],
88
+ route: c[:route],
88
89
  actions: []
89
90
  }
90
91
  end
@@ -31,7 +31,7 @@ Building a better future, one line of code at a time.
31
31
  =end
32
32
 
33
33
  module Lesli
34
- class User::Role < ApplicationLesliRecord
34
+ class User::Power < ApplicationLesliRecord
35
35
  belongs_to :user
36
36
  belongs_to :role
37
37
  has_many :roles
@@ -31,15 +31,16 @@ Building a better future, one line of code at a time.
31
31
  =end
32
32
 
33
33
  module Lesli
34
- class RoleService < ApplicationLesliService
35
-
36
- # Return a list of roles that the user is able to work with
37
- # according to object level permission
38
- def list params
39
- current_user.account.roles
40
- .where("object_level_permission <= ?", current_user.max_object_level_permission)
41
- .order(object_level_permission: :desc, name: :asc)
42
- .select(:id, :name, :object_level_permission)
34
+ class User::Setting < ApplicationRecord
35
+ belongs_to :user
36
+
37
+ validates :name, presence: true, on: :create
38
+ validates :value, presence: true, on: :create
39
+
40
+ after_update :after_update_settings
41
+
42
+ def after_update_settings
43
+ #Courier::One::Firebase::User.sync_user(self.user)
43
44
  end
44
45
  end
45
46
  end
@@ -33,10 +33,9 @@ Building a better future, one line of code at a time.
33
33
  module Lesli
34
34
  class User < ApplicationLesliRecord
35
35
 
36
- include UserGuard
36
+ include UserSecurity
37
37
  include UserExtensions
38
38
  #include UserActivities
39
- #include UserPolyfill
40
39
 
41
40
  # users belongs to an account only... and must have a role
42
41
  belongs_to :account, optional: true
@@ -57,9 +56,10 @@ module Lesli
57
56
  has_many :activities, class_name: "User::Activity"
58
57
 
59
58
  # users can have many roles and too many privileges through the roles
60
- has_many :user_roles, class_name: "Lesli::User::Role"
61
- has_many :roles, class_name: "Lesli::Role", through: :user_roles, source: :role
62
- #has_many :privileges, through: :roles
59
+ # every role adds a power to the user, power is just a role id
60
+ has_many :powers
61
+ has_many :roles, through: :powers, source: :role, class_name: "Lesli::Role"
62
+ has_many :privileges, through: :roles, class_name: "Lesli::Role::Privilege"
63
63
 
64
64
 
65
65
  # devise implementation
@@ -88,7 +88,6 @@ module Lesli
88
88
 
89
89
  # callbacks
90
90
  before_create :before_create_user
91
- after_create :after_create_user
92
91
  #after_create :after_confirmation_user, if: :confirmed?
93
92
  #after_create :after_account_assignation
94
93
  #after_update :update_associated_services
@@ -108,26 +107,18 @@ module Lesli
108
107
  end
109
108
 
110
109
 
111
- # @return [void]
112
- # @description After creating a user, creates the necessary resources for them to access the different engines.
113
- # At the current time, it only creates a default calendar. This is an *after_create* method, and is not
114
- # designed to be invoked directly
115
- def after_create_user
116
-
117
- # create user details
118
- #User::Detail.find_or_create_by({ user: self })
110
+ # Initialize user settings and dependencies needed
111
+ def after_confirmation_user
112
+ return unless self.confirmed?
119
113
 
120
114
  # create an alias based on user name
121
115
  # defined in user extensions
122
116
  self.set_alias
123
117
 
124
- end
125
-
126
-
127
- # Initialize user settings and dependencies needed
128
- def after_confirmation_user
129
- return unless self.confirmed?
118
+ # create user details
119
+ #User::Detail.find_or_create_by({ user: self })
130
120
 
121
+ # Minimum security settings required
131
122
  self.settings.create_with(:value => false).find_or_create_by(:name => "mfa_enabled")
132
123
  self.settings.create_with(:value => :email).find_or_create_by(:name => "mfa_method")
133
124
  end
@@ -0,0 +1,148 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+
33
+ module Lesli
34
+ class ControllerOperator < Lesli::ApplicationLesliService
35
+
36
+ DEVISE_CONTROLLERS = [
37
+ "users/registrations",
38
+ "users/sessions",
39
+ "users/passwords",
40
+ "users/confirmations"
41
+ ]
42
+
43
+ def initialize
44
+ end
45
+
46
+ "Scan new routes added and create role privileges"
47
+ def build
48
+
49
+ # get all the engines, controllers and actions
50
+ engines = scan_for_engine_controllers
51
+
52
+ # Register descriptors and privileges for all the accounts
53
+ engines.each do |engine, controllers|
54
+
55
+ controllers.each do |controller_route, controller_actions|
56
+
57
+ # Build a strig with the standard name of a Rails controller from the standard routes
58
+ # Examples:
59
+ # users converts to Users
60
+ # cloud_bell/notifications converts to CloudBell::Notifications
61
+ # sometimes we need a second split to deal with third level deep of controllers
62
+ # Example: "Account::Currency::ExchangeRatesController" from "account/currency/exchange_rates"
63
+ reference = controller_route
64
+ .split('/') # split the controller path by namespace
65
+ .collect(&:capitalize) # uppercase the first letter to match the class name convention of Rails
66
+ .join("::") # join by ruby class separator for namespaces
67
+ .split('_') # work with compound words like "exchange_rates"
68
+ .collect { |x| x[0] = x[0].upcase; x } # convert ['exchange', 'rates'] to ['Exchange', 'Rates']
69
+ .join('') # joins everything in a single string
70
+
71
+ name = reference.sub('::',' ')
72
+
73
+ controller = Lesli::SystemController.create_with({
74
+ name: name,
75
+ :engine => engine,
76
+ :reference => reference
77
+ }).find_or_create_by!(route: controller_route)
78
+
79
+ controller_actions.each do |action_name|
80
+ controller.actions.find_or_create_by!(name: action_name)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def scan_for_engine_controllers
89
+
90
+ # Global container
91
+ controller_list = {
92
+ "app" => {},
93
+ "lesli" => {}
94
+ }
95
+
96
+ # Get the list of controllers and actions of the main rails app
97
+ Rails.application.routes.routes.each do |route|
98
+
99
+ list = "app"
100
+ route = route.defaults
101
+
102
+ # filter the non-used main app routes
103
+ next if route[:controller].blank?
104
+ next if route[:controller].include? "rails"
105
+ next if route[:controller].include? "action_mailbox"
106
+ next if route[:controller].include? "active_storage"
107
+
108
+ if DEVISE_CONTROLLERS.include?(route[:controller])
109
+ list = "lesli"
110
+ end
111
+
112
+ # create a container for the actions related to a controller
113
+ controller_list[list][route[:controller]] = [] unless controller_list[list][route[:controller]]
114
+
115
+ # assign and group all the actions related to the controller
116
+ controller_list[list][route[:controller]].push(route[:action])
117
+
118
+ end
119
+
120
+ # Get the list of controllers and actions from engines
121
+ Lesli::System.engines.each do |engine, engine_info|
122
+
123
+ # load and retrieve the list of controllers and actions from an engine
124
+ routes = "#{engine}::Engine".constantize.routes.routes.each do |route|
125
+ route = route.defaults
126
+
127
+ # validate if route has information, some special routes like redirects
128
+ # can generate an empty entry in the route hash
129
+ next if route.empty?
130
+
131
+ # get the engine code
132
+ engine_code = engine_info[:code]
133
+
134
+ # create a container for the controllers related to the engine
135
+ controller_list[engine_code] = {} if controller_list[engine_code].blank?
136
+
137
+ # assign and group all the actions related to the controller
138
+ controller_list[engine_code][route[:controller]] = [] if controller_list[engine_code][route[:controller]].blank?
139
+
140
+ # assign and group all the actions related to the controller
141
+ controller_list[engine_code][route[:controller]].push(route[:action])
142
+ end
143
+ end
144
+
145
+ return controller_list
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,75 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+
33
+ module Lesli
34
+ class DescriptorPrivilegeOperator < Lesli::ApplicationLesliService
35
+
36
+ @descriptor = nil
37
+
38
+ def initialize descriptor
39
+ @descriptor = descriptor
40
+ end
41
+
42
+ def add_profile_privileges(descriptor)
43
+
44
+ # Adding default system actions for profile descriptor
45
+ [
46
+ { controller: "lesli_admin/profiles", actions: ["show"] }, # enable profile view
47
+ { controller: "lesli/users", actions: ["options", "update"] }, # enable user edition
48
+ { controller: "lesli/abouts", actions: ["show"] }, # system status
49
+ { controller: "lesli/user/sessions", actions: ["index"] } # session management
50
+ ].each do |controller_action|
51
+
52
+ controller_action[:actions].each do |action_name|
53
+
54
+ system_controller_action = SystemController::Action.joins(:system_controller)
55
+ .where("lesli_system_controllers.route = ?", controller_action[:controller])
56
+ .where("lesli_system_controller_actions.name = ?", action_name)
57
+
58
+ descriptor.privileges.find_or_create_by(
59
+ action: system_controller_action.first
60
+ )
61
+ end
62
+ end
63
+ end
64
+
65
+ def add_owner_privileges(descriptor)
66
+
67
+ # Adding default system actions for profile descriptor
68
+ actions = SystemController::Action.all
69
+
70
+ actions.each do |action|
71
+ descriptor.privileges.find_or_create_by(action: action)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,108 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+
33
+ module Lesli
34
+ class RolePowerOperator < Lesli::ApplicationLesliService
35
+
36
+ @roles;
37
+
38
+ def initialize *roles
39
+ @roles = roles
40
+ end
41
+
42
+ # Syncronize the descriptor privileges with the role privilege cache table
43
+ def synchronize
44
+
45
+ # bulk all the descriptor privileges
46
+ # this script was built manually for performance, maintenance
47
+ # and to make it easy to read for future changes, basically what it does
48
+ # is get the controllers and actions assigned to a descriptor through the
49
+ # system_descriptor_privileges table and create an array of hashes with
50
+ # all the raw privileges (this includes duplicated privileges)
51
+ records = Descriptor.joins(%(
52
+ INNER JOIN lesli_descriptor_privileges
53
+ ON lesli_descriptor_privileges.descriptor_id = lesli_descriptors.id
54
+ )).joins(%(
55
+ INNER JOIN lesli_system_controller_actions
56
+ ON lesli_system_controller_actions.id = lesli_descriptor_privileges.action_id
57
+ )).joins(%(
58
+ INNER JOIN lesli_system_controllers
59
+ ON lesli_system_controllers.id = lesli_system_controller_actions.system_controller_id
60
+ )).joins(%(
61
+ INNER JOIN lesli_role_powers
62
+ ON lesli_role_powers.descriptor_id = lesli_descriptors.id
63
+ )).select(
64
+ "lesli_system_controllers.route as controller",
65
+ "lesli_system_controller_actions.name as action",
66
+ "case when lesli_role_powers.deleted_at is null then true else false end as active",
67
+ "lesli_role_powers.role_id as role_id"
68
+ ).with_deleted
69
+
70
+
71
+ # get privileges only for the given role, this is needed to sync only modified roles
72
+ records = records.where("lesli_role_powers.role_id" => @roles)
73
+
74
+ # we use the deleted_at column to know if a privilege is enable or disable, NULL values
75
+ # at the deleted_at column means privilege is active, so if we sort by deleted_at column
76
+ # all the active privileges will be at the top, then the uniq method is going to take
77
+ # always the active values, to completely disable a privilege for a specific controller/action
78
+ # we have to disable in all the descriptors
79
+ records = records.order("lesli_role_powers.deleted_at DESC")
80
+
81
+ # convert the results to json so it is easy to insert/update
82
+ records = records.as_json(only: [:controller, :action, :role_id, :active])
83
+
84
+ # IMPORTANT: We must save only uniq privileges in the role_privilege table
85
+ # this means that it does not matters how many times we defined a privilege dependency
86
+ # we insert the privilege only once.
87
+ # Example: If we defined that we need access to UsersController#index in 20 descriptors,
88
+ # in the role_privileges will be only one record for that specific controller and action
89
+ records = records.uniq do |privilege|
90
+
91
+ # NOTE: If can disable a privilege that belongs to a descriptor,
92
+ # however, if the same privilege is define in another active descriptor,
93
+ # the role that has both descriptor will be able to access the resources
94
+ # of that privilege, that is a normal and desire behavior.
95
+ [privilege["controller"], privilege["action"], privilege["role_id"]]
96
+ end
97
+
98
+ # small check to ensure I have records to update/insert
99
+ return if records.blank?
100
+
101
+ # bulk update/insert into role privilege cache table
102
+ # IMPORTANT: Due to the importance and how delicate this process is, it is better
103
+ # to copy the controller name and actions from the system, instead of
104
+ # just have a reference to the system_controller_actions table
105
+ Lesli::Role::Privilege.with_deleted.upsert_all(records, unique_by: [:controller, :action, :role_id])
106
+ end
107
+ end
108
+ end