anoubis 1.0.0

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 (188) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +38 -0
  5. data/app/controllers/anoubis/application_controller.rb +78 -0
  6. data/app/controllers/anoubis/core/application_controller.rb +343 -0
  7. data/app/controllers/anoubis/core/data/actions.rb +962 -0
  8. data/app/controllers/anoubis/core/data/callbacks.rb +68 -0
  9. data/app/controllers/anoubis/core/data/convert.rb +407 -0
  10. data/app/controllers/anoubis/core/data/defaults.rb +217 -0
  11. data/app/controllers/anoubis/core/data/get.rb +531 -0
  12. data/app/controllers/anoubis/core/data/load.rb +89 -0
  13. data/app/controllers/anoubis/core/data/set.rb +49 -0
  14. data/app/controllers/anoubis/core/data/setup.rb +104 -0
  15. data/app/controllers/anoubis/core/data_controller.rb +28 -0
  16. data/app/controllers/anoubis/core/index/actions.rb +53 -0
  17. data/app/controllers/anoubis/core/index/callbacks.rb +23 -0
  18. data/app/controllers/anoubis/core/index_controller.rb +36 -0
  19. data/app/controllers/anoubis/etc/base.rb +52 -0
  20. data/app/controllers/anoubis/etc/data.rb +89 -0
  21. data/app/controllers/anoubis/etc/field.rb +468 -0
  22. data/app/controllers/anoubis/etc/field_options.rb +83 -0
  23. data/app/controllers/anoubis/etc/field_order.rb +51 -0
  24. data/app/controllers/anoubis/etc/filter.rb +251 -0
  25. data/app/controllers/anoubis/etc/menu.rb +101 -0
  26. data/app/controllers/anoubis/etc/model.rb +67 -0
  27. data/app/controllers/anoubis/etc/tab_item.rb +91 -0
  28. data/app/controllers/anoubis/etc.rb +8 -0
  29. data/app/controllers/anoubis/export.rb +47 -0
  30. data/app/controllers/anoubis/output/autocomplete.rb +30 -0
  31. data/app/controllers/anoubis/output/basic.rb +86 -0
  32. data/app/controllers/anoubis/output/data.rb +101 -0
  33. data/app/controllers/anoubis/output/delete.rb +41 -0
  34. data/app/controllers/anoubis/output/edit.rb +55 -0
  35. data/app/controllers/anoubis/output/frame.rb +227 -0
  36. data/app/controllers/anoubis/output/login.rb +71 -0
  37. data/app/controllers/anoubis/output/menu.rb +220 -0
  38. data/app/controllers/anoubis/output/update.rb +43 -0
  39. data/app/controllers/anoubis/sso/client/application_controller.rb +139 -0
  40. data/app/controllers/anoubis/sso/client/data/actions.rb +5 -0
  41. data/app/controllers/anoubis/sso/client/data/callbacks.rb +5 -0
  42. data/app/controllers/anoubis/sso/client/data/convert.rb +5 -0
  43. data/app/controllers/anoubis/sso/client/data/defaults.rb +5 -0
  44. data/app/controllers/anoubis/sso/client/data/get.rb +5 -0
  45. data/app/controllers/anoubis/sso/client/data/load.rb +26 -0
  46. data/app/controllers/anoubis/sso/client/data/set.rb +5 -0
  47. data/app/controllers/anoubis/sso/client/data/setup.rb +5 -0
  48. data/app/controllers/anoubis/sso/client/data_controller.rb +21 -0
  49. data/app/controllers/anoubis/sso/client/index/actions.rb +79 -0
  50. data/app/controllers/anoubis/sso/client/index/callbacks.rb +13 -0
  51. data/app/controllers/anoubis/sso/client/index_controller.rb +18 -0
  52. data/app/controllers/anoubis/sso/server/application_controller.rb +49 -0
  53. data/app/controllers/anoubis/sso/server/login_controller.rb +342 -0
  54. data/app/controllers/anoubis/sso/server/user_controller.rb +142 -0
  55. data/app/controllers/anoubis/tenant/application_controller.rb +54 -0
  56. data/app/controllers/anoubis/tenant/data/actions.rb +11 -0
  57. data/app/controllers/anoubis/tenant/data/callbacks.rb +11 -0
  58. data/app/controllers/anoubis/tenant/data/convert.rb +11 -0
  59. data/app/controllers/anoubis/tenant/data/defaults.rb +11 -0
  60. data/app/controllers/anoubis/tenant/data/get.rb +11 -0
  61. data/app/controllers/anoubis/tenant/data/load.rb +52 -0
  62. data/app/controllers/anoubis/tenant/data/set.rb +11 -0
  63. data/app/controllers/anoubis/tenant/data/setup.rb +11 -0
  64. data/app/controllers/anoubis/tenant/data_controller.rb +28 -0
  65. data/app/controllers/anoubis/tenant/index/actions.rb +191 -0
  66. data/app/controllers/anoubis/tenant/index/callbacks.rb +11 -0
  67. data/app/controllers/anoubis/tenant/index_controller.rb +38 -0
  68. data/app/controllers/anoubis/tenants_controller.rb +7 -0
  69. data/app/controllers/anoubis/users_controller.rb +7 -0
  70. data/app/jobs/anoubis/application_job.rb +6 -0
  71. data/app/mailers/anoubis/application_mailer.rb +8 -0
  72. data/app/models/anoubis/application_record.rb +45 -0
  73. data/app/models/anoubis/core/application_record.rb +250 -0
  74. data/app/models/anoubis/core/locales.rb +27 -0
  75. data/app/models/anoubis/sso/client/application_record.rb +3 -0
  76. data/app/models/anoubis/sso/client/group.rb +19 -0
  77. data/app/models/anoubis/sso/client/group_menu.rb +109 -0
  78. data/app/models/anoubis/sso/client/menu.rb +145 -0
  79. data/app/models/anoubis/sso/client/user.rb +81 -0
  80. data/app/models/anoubis/sso/client/user_group.rb +32 -0
  81. data/app/models/anoubis/sso/server/system.rb +36 -0
  82. data/app/models/anoubis/sso/server/user.rb +79 -0
  83. data/app/models/anoubis/tenant/application_record.rb +41 -0
  84. data/app/models/anoubis/tenant/group.rb +95 -0
  85. data/app/models/anoubis/tenant/group_locale.rb +19 -0
  86. data/app/models/anoubis/tenant/group_menu.rb +84 -0
  87. data/app/models/anoubis/tenant/menu.rb +156 -0
  88. data/app/models/anoubis/tenant/menu_locale.rb +27 -0
  89. data/app/models/anoubis/tenant/system.rb +127 -0
  90. data/app/models/anoubis/tenant/system_locale.rb +19 -0
  91. data/app/models/anoubis/tenant/system_menu.rb +51 -0
  92. data/app/models/anoubis/tenant/tenant.rb +107 -0
  93. data/app/models/anoubis/tenant/tenant_system.rb +19 -0
  94. data/app/models/anoubis/tenant/user.rb +225 -0
  95. data/app/models/anoubis/tenant/user_group.rb +32 -0
  96. data/app/services/anoubis/core_service.rb +16 -0
  97. data/app/services/anoubis/session_service.rb +17 -0
  98. data/app/validators/presence_in_tenant_validator.rb +20 -0
  99. data/config/initializers/mime_type.rb +1 -0
  100. data/config/locales/en.yml +120 -0
  101. data/config/locales/ru.yml +245 -0
  102. data/config/routes.rb +74 -0
  103. data/db/migrate/20181018085843_create_tenants.rb +13 -0
  104. data/db/migrate/20181018111217_create_systems.rb +10 -0
  105. data/db/migrate/20181018111713_create_tenant_systems.rb +11 -0
  106. data/db/migrate/20181018111925_create_groups.rb +13 -0
  107. data/db/migrate/20181018112151_create_users.rb +25 -0
  108. data/db/migrate/20181018115737_add_title_to_users.rb +10 -0
  109. data/db/migrate/20181022060211_create_menus.rb +18 -0
  110. data/db/migrate/20181115055245_create_group_menus.rb +12 -0
  111. data/db/migrate/20181115060830_create_system_menus.rb +11 -0
  112. data/db/migrate/20181122062131_create_user_groups.rb +11 -0
  113. data/db/migrate/20181221060727_create_menu_locales.rb +14 -0
  114. data/db/migrate/20181225062303_create_system_locales.rb +11 -0
  115. data/db/migrate/20181225062339_create_group_locales.rb +11 -0
  116. data/db/seeds.rb +268 -0
  117. data/lib/anoubis/engine.rb +13 -0
  118. data/lib/anoubis/version.rb +5 -0
  119. data/lib/anoubis.rb +213 -0
  120. data/lib/tasks/anubis_tasks.rake +10 -0
  121. data/lib/tasks/sessions/clear_sessions.rake +10 -0
  122. data/spec/anubis_spec.rb +5 -0
  123. data/spec/controllers/anoubis/index_controller_spec.rb +77 -0
  124. data/spec/dummy/Rakefile +3 -0
  125. data/spec/dummy/app/assets/config/manifest.js +2 -0
  126. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  127. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  128. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  129. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  130. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  131. data/spec/dummy/app/jobs/application_job.rb +2 -0
  132. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  133. data/spec/dummy/app/models/application_record.rb +3 -0
  134. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  135. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  136. data/spec/dummy/bin/bundle +3 -0
  137. data/spec/dummy/bin/rails +4 -0
  138. data/spec/dummy/bin/rake +4 -0
  139. data/spec/dummy/bin/setup +33 -0
  140. data/spec/dummy/bin/update +28 -0
  141. data/spec/dummy/config/application.rb +14 -0
  142. data/spec/dummy/config/boot.rb +5 -0
  143. data/spec/dummy/config/cable.yml +10 -0
  144. data/spec/dummy/config/database.yml +54 -0
  145. data/spec/dummy/config/environment.rb +5 -0
  146. data/spec/dummy/config/environments/development.rb +54 -0
  147. data/spec/dummy/config/environments/production.rb +85 -0
  148. data/spec/dummy/config/environments/test.rb +46 -0
  149. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  150. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  151. data/spec/dummy/config/initializers/cors.rb +16 -0
  152. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  153. data/spec/dummy/config/initializers/inflections.rb +16 -0
  154. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  155. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  156. data/spec/dummy/config/locales/en.yml +33 -0
  157. data/spec/dummy/config/puma.rb +34 -0
  158. data/spec/dummy/config/routes.rb +3 -0
  159. data/spec/dummy/config/spring.rb +6 -0
  160. data/spec/dummy/config/storage.yml +34 -0
  161. data/spec/dummy/config.ru +5 -0
  162. data/spec/dummy/db/schema.rb +167 -0
  163. data/spec/dummy/db/seeds.rb +1 -0
  164. data/spec/factories/anubis_group_locales.rb +7 -0
  165. data/spec/factories/anubis_group_menus.rb +7 -0
  166. data/spec/factories/anubis_groups.rb +6 -0
  167. data/spec/factories/anubis_menu_locales.rb +9 -0
  168. data/spec/factories/anubis_menus.rb +6 -0
  169. data/spec/factories/anubis_system_locales.rb +7 -0
  170. data/spec/factories/anubis_system_menus.rb +6 -0
  171. data/spec/factories/anubis_systems.rb +5 -0
  172. data/spec/factories/anubis_tenants.rb +7 -0
  173. data/spec/factories/anubis_users.rb +10 -0
  174. data/spec/integration/navigation_test.rb +7 -0
  175. data/spec/models/anoubis/group_locale_spec.rb +25 -0
  176. data/spec/models/anoubis/group_menu_spec.rb +50 -0
  177. data/spec/models/anoubis/group_spec.rb +52 -0
  178. data/spec/models/anoubis/menu_locale_spec.rb +31 -0
  179. data/spec/models/anoubis/menu_spec.rb +48 -0
  180. data/spec/models/anoubis/system_locale_spec.rb +20 -0
  181. data/spec/models/anoubis/system_menu_spec.rb +49 -0
  182. data/spec/models/anoubis/system_spec.rb +53 -0
  183. data/spec/models/anoubis/tenant_spec.rb +67 -0
  184. data/spec/models/anoubis/user_spec.rb +57 -0
  185. data/spec/rails_helper.rb +32 -0
  186. data/spec/requests/anoubis/users_request_spec.rb +5 -0
  187. data/spec/spec_helper.rb +13 -0
  188. metadata +408 -0
@@ -0,0 +1,109 @@
1
+ ##
2
+ # Model links {Menu} and {Group}. Describes group access to menu.
3
+ class Anubis::Sso::Client::GroupMenu < Anubis::Sso::Client::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'group_menus'
6
+
7
+ before_validation :before_validation_sso_client_group_menu
8
+ after_create :after_create_sso_client_group_menu
9
+ before_update :before_update_sso_client_group_menu
10
+ after_destroy :after_destroy_sso_client_group_menu
11
+
12
+
13
+ # @!attribute group
14
+ # @return [Group] reference to the {Group} model
15
+ belongs_to :group, :class_name => 'Anubis::Sso::Client::Group'
16
+ validates :group, presence: true, uniqueness: { scope: [:menu_id] }
17
+
18
+ # @!attribute menu
19
+ # @return [Menu] reference to the {Menu} model
20
+ belongs_to :menu, :class_name => 'Anubis::Sso::Client::Menu'
21
+ validates :menu, presence: true, uniqueness: { scope: [:group_id] }
22
+
23
+ # @!attribute access
24
+ # @return ['not', 'read', 'write', 'disable'] group access to menu element.
25
+ # - 'not' --- menu element doesn't available for this group
26
+ # - 'read' --- group has access to menu element only for read data
27
+ # - 'write' --- group has access to menu element for read and write data
28
+ # - 'disable' --- group hasn't access to menu element
29
+ enum access: { not: 0, read: 20, write: 40, disable: 60 }
30
+
31
+ ##
32
+ # Is called before validation when the link between menu and group is being created or updated.
33
+ # Procedure checks if group belongs a system that has access to this menu element. If {#access} doesn't
34
+ # defined then {#access} sets to 'read'
35
+ def before_validation_sso_client_group_menu
36
+ self.access = 'read' if !self.access
37
+ end
38
+
39
+ ##
40
+ # Is called after new link between menu and group was created. If new element has parent with link that
41
+ # doesn't present in database then adds this link to database with {#access} defined as 'read'.
42
+ def after_create_sso_client_group_menu
43
+ if self.menu.menu_id != nil
44
+ Anubis::Sso::Client::GroupMenu.find_or_create_by(menu_id: self.menu.menu_id, group_id: self.group_id) do |menu|
45
+ menu.access = 'read'
46
+ end
47
+ end
48
+ self.after_modify_sso_client_group_menu
49
+ end
50
+
51
+ ##
52
+ # Is called before link between menu and group will be updated. Procedure prevents changing {#menu}
53
+ # and {#group} value.
54
+ def before_update_sso_client_group_menu
55
+ self.menu_id = self.menu_id_was if self.menu_id_changed?
56
+ self.group_id = self.group_id_was if self.group_id_changed?
57
+ self.after_modify_sso_client_group_menu
58
+ end
59
+
60
+ ##
61
+ # Is called after link between menu and group had been deleted from database. It also deletes all child links.
62
+ def after_destroy_sso_client_group_menu
63
+ Anubis::Sso::Client::Menu.select(:id).where(menu_id: self.menu_id).each do |menu|
64
+ Anubis::Sso::Client::GroupMenu.where(menu_id: menu.id, group_id: self.group_id).each do |group_menu|
65
+ group_menu.destroy
66
+ end
67
+ end
68
+ self.after_modify_sso_client_group_menu
69
+ end
70
+
71
+ ##
72
+ # Deletes all user's keys that belong this menu element in Redis database.
73
+ def after_modify_sso_client_group_menu
74
+ if self.redis
75
+ self.redis.keys(self.redis_prefix + '*_' + self.menu.mode).each do |data|
76
+ self.redis.del data
77
+ end
78
+ end
79
+ end
80
+
81
+ def title
82
+ self.get_localized_menu 'title'
83
+ end
84
+
85
+ def page_title
86
+ self.get_localized_menu 'page_title'
87
+ end
88
+
89
+ def short_title
90
+ self.get_localized_menu 'short_title'
91
+ end
92
+
93
+ def get_localized_menu(field)
94
+ loc_field = (field.to_s + '_locale').to_sym
95
+ begin
96
+ self[loc_field] = JSON.parse(self[loc_field]) if self[loc_field]
97
+ rescue
98
+
99
+ end
100
+
101
+ begin
102
+ result = self.get_locale_field loc_field.to_s
103
+ rescue
104
+ result = eval('self.menu.' + field.to_s)
105
+ end
106
+
107
+ result
108
+ end
109
+ end
@@ -0,0 +1,145 @@
1
+ class Anubis::Sso::Client::Menu < Anubis::Sso::Client::ApplicationRecord
2
+ self.table_name = 'menus'
3
+
4
+ before_create :before_create_sso_client_menu
5
+ before_update :before_update_sso_client_menu
6
+ before_save :before_save_sso_client_menu
7
+ before_destroy :before_destroy_sso_client_menu
8
+ after_destroy :after_destroy_sso_client_menu
9
+
10
+ VALID_IDENT_REGEX = /\A[a-z_\/0-9]*\z/i
11
+
12
+ # @!attribute mode
13
+ # @return [String] the controller path for menu element.
14
+ validates :mode, length: { minimum: 3, maximum: 100 }, uniqueness: { case_sensitive: false }, format: { with: VALID_IDENT_REGEX }
15
+
16
+ # @!attribute action
17
+ # @return [String] the default action of menu element ('data', 'menu', etc.).
18
+ validates :action, presence: true
19
+
20
+ # @!attribute tab
21
+ # @return [Integer] the nesting level of menu element
22
+ validates :tab, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
23
+
24
+ # @!attribute position
25
+ # @return [Integer] the order position of menu element in current level.
26
+ validates :position, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
27
+
28
+ # @!attribute page_size
29
+ # @return [Integer] the default page size for table of data frame.
30
+ validates :page_size, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
31
+
32
+ # @!attribute menu
33
+ # @return [Menu, nil] the parent menu for element menu (if exists).
34
+ belongs_to :menu, class_name: 'Anubis::Sso::Client::Menu', optional: true
35
+ has_many :menus, class_name: 'Anubis::Sso::Client::Menu'
36
+
37
+ # @!attribute title
38
+ # @return [String] the menu's localized title.
39
+ validates :title, presence: true, length: { maximum: 100 }
40
+
41
+ def title
42
+ get_locale_field 'title_locale'
43
+ end
44
+
45
+ def title=(value)
46
+ self.set_locale_field 'title_locale', value
47
+ end
48
+
49
+ # @!attribute page_title
50
+ # @return [String] the menu's localized page title. Uses in frontend application.
51
+ validates :page_title, presence: true, length: { minimum: 3, maximum: 200 }
52
+
53
+ def page_title
54
+ get_locale_field 'page_title_locale'
55
+ end
56
+
57
+ def page_title=(value)
58
+ self.set_locale_field 'page_title_locale', value
59
+ end
60
+
61
+ # @!attribute short_title
62
+ # @return [String] the menu's localized short title. Uses in frontend application.
63
+ validates :short_title, length: { maximum: 200 }
64
+
65
+ def short_title
66
+ get_locale_field 'short_title_locale'
67
+ end
68
+
69
+ def short_title=(value)
70
+ self.set_locale_field 'short_title_locale', value
71
+ end
72
+
73
+ # @!attribute status
74
+ # @return ['enabled', 'disabled'] the status of menu element.
75
+ # - 'enabled' --- element is enabled and is used by the system.
76
+ # - 'disabled' --- element is disabled and isn't used by the system.
77
+ enum status: { enabled: 0, disabled: 1 }
78
+
79
+ # @!attribute state
80
+ # @return ['visible', 'hidden'] the visibility of menu element. Attribute is used in fronted application.
81
+ # - 'visible' --- element is visible.
82
+ # - 'hidden' --- element is hidden.
83
+ enum state: { visible: 0, hidden: 1 }
84
+
85
+ has_many :group_menus, class_name: 'Anubis::Sso::Client::GroupMenu'
86
+
87
+ ##
88
+ # Is called before menu will be created in database. Sets {#position} as last {#position} + 1 on current {#tab}.
89
+ # After this calls {#before_update_menu} for additional modification.
90
+ def before_create_sso_client_menu
91
+ data = Anubis::Sso::Client::Menu.where(menu_id: self.menu_id).maximum(:position)
92
+ self.position = if data then data + 1 else 0 end
93
+
94
+ self.before_update_sso_client_menu
95
+ end
96
+
97
+ ##
98
+ # Is called before menu will be stored in database. Sets {#mode} and {#action} in lowercase. If {#page_size}
99
+ # doesn't defined then sets it to 20. If defined parent menu element then sets {#tab} based on {#tab} of
100
+ # parent menu element + 1.
101
+ def before_update_sso_client_menu
102
+ self.mode = mode.downcase
103
+ self.action = self.action.downcase
104
+ self.page_size = 20 if self.page_size == 0
105
+ self.page_size = self.page_size.to_i
106
+
107
+ parent_menu = Anubis::Sso::Client::Menu.where(id: self.menu_id).first
108
+ if parent_menu
109
+ self.tab = parent_menu.tab + 1
110
+ else
111
+ self.tab = 0
112
+ end
113
+ end
114
+
115
+ ##
116
+ # Is called right before menu will be stored in database (after {#before_create_menu} and {#before_update_menu}).
117
+ # Deletes cache data for this menu in Redis database.
118
+ def before_save_sso_client_menu
119
+ self.redis.del(self.redis_prefix + 'menu:' + self.mode) if self.redis
120
+ end
121
+
122
+ ##
123
+ # Is called before menu will be deleted from database. Checks the ability to destroy a menu. Delete
124
+ # all translations for menu model from {MenuLocale}.
125
+ def before_destroy_sso_client_menu
126
+ if !self.can_destroy?
127
+ errors.add(:base, I18n.t('anubis.menus.errors.has_childs'))
128
+ throw(:abort, __method__)
129
+ end
130
+ end
131
+
132
+ ##
133
+ # Is called after menu was deleted from database. Procedure recalculates position of other menu elements.
134
+ def after_destroy_sso_client_menu
135
+ query = <<-SQL
136
+ UPDATE menus
137
+ SET menus.position = menus.position - 1
138
+ WHERE menus.tab = #{self.tab} AND menus.position > #{self.position}
139
+ SQL
140
+ Anubis::Sso::Client::Menu.connection.execute query
141
+ Anubis::Sso::Client::Menu.where(menu_id: self.id).find_each do |menu|
142
+ menu.destroy
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,81 @@
1
+ class Anubis::Sso::Client::User < Anubis::Sso::Client::ApplicationRecord
2
+ self.table_name = 'users'
3
+
4
+ after_destroy :after_destroy_anubis_sso_client_user
5
+ after_save :after_save_anubis_sso_client_user
6
+
7
+ attr_accessor :name
8
+ attr_accessor :surname
9
+ attr_accessor :locale
10
+ attr_accessor :timeout
11
+ attr_accessor :timezone
12
+ attr_accessor :menus
13
+
14
+ def save_cache(sso_data)
15
+ if sso_data
16
+ self.name = sso_data[:name] if sso_data.key? :name
17
+ self.surname = sso_data[:surname] if sso_data.key? :surname
18
+ self.locale = sso_data[:locale] if sso_data.key? :locale
19
+ self.timeout = sso_data[:timeout] if sso_data.key? :timeout
20
+ self.timezone = sso_data[:timezone] if sso_data.key? :timezone
21
+ end
22
+ self.redis.set(self.redis_prefix + 'user:' + self.uuid, self.to_json) if self.redis
23
+ end
24
+
25
+ def clear_cache
26
+ self.redis.del(self.redis_prefix + 'user:' + self.uuid) if self.redis
27
+ end
28
+
29
+ def self.load_cache(redis, uuid)
30
+ begin
31
+ data = JSON.parse redis.get(User.redis_prefix + 'user:' + uuid), { symbolize_names: true }
32
+ rescue
33
+ data = nil
34
+ end
35
+
36
+ unless data
37
+ user = self.where(uuid: uuid).first
38
+ if user
39
+ return JSON.parse(user.to_json(except: [:password_digest]), { symbolize_names: true })
40
+ end
41
+ end
42
+
43
+ data
44
+ end
45
+
46
+ def attributes
47
+ super.merge({
48
+ name: self.name,
49
+ surname: self.surname,
50
+ locale: self.locale,
51
+ timeout: self.timeout,
52
+ timezone: self.timezone,
53
+ menus: self.get_menus
54
+ })
55
+ end
56
+
57
+ def after_save_anubis_sso_client_user
58
+ self.clear_cache
59
+ end
60
+
61
+ def after_destroy_anubis_sso_client_user
62
+ self.clear_cache
63
+ end
64
+
65
+ def get_menus
66
+ self.menus = {}
67
+ access = Anubis::Sso::Client::GroupMenu.accesses[:read].to_s+','+Anubis::Sso::Client::GroupMenu.accesses[:write].to_s
68
+ query = <<-SQL
69
+ SELECT `menus`.`id`, `menus`.`mode`, MAX(`group_menus`.`access`) AS `access`
70
+ FROM (`menus`, `group_menus`, `groups`, `user_groups`)
71
+ WHERE `menus`.`status` = 0 AND `menus`.`id` = `group_menus`.`menu_id` AND `group_menus`.`access` IN (#{access}) AND `group_menus`.`group_id` = `groups`.`id` AND
72
+ `groups`.`id` = `user_groups`.`group_id` AND `user_groups`.`user_id` = #{self.id}
73
+ GROUP BY `menus`.`id`, `menus`.`mode`
74
+ SQL
75
+ Anubis::Sso::Client::GroupMenu.find_by_sql(query).each do |data|
76
+ self.menus[data[:mode]] = data.access
77
+ end
78
+
79
+ self.menus
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ ##
2
+ # UserGroup model. It links User and Group models.
3
+ class Anoubis::Sso::Client::UserGroup < Anoubis::Sso::Client::ApplicationRecord
4
+ self.table_name = 'user_groups'
5
+
6
+ before_update :before_update_sso_client_user_group
7
+ after_create :after_modify_sso_client_user_group
8
+ after_destroy :after_modify_sso_client_user_group
9
+
10
+ belongs_to :group, class_name: 'Anoubis::Sso::Client::Group'
11
+ validates :group, presence: true, uniqueness: { scope: [:user_id] }
12
+ belongs_to :user, class_name: 'Anoubis::Sso::Client::User'
13
+ validates :user, presence: true, uniqueness: { scope: [:group_id] }
14
+
15
+ ##
16
+ # Can't change elements
17
+ def before_update_sso_client_user_group
18
+ self.user_id = self.user_id_was if self.user_id_changed?
19
+ self.group_id = self.group_id_was if self.group_id_changed?
20
+ end
21
+
22
+ ##
23
+ # Delete all redis keys of menu for defined user
24
+ def after_modify_sso_client_user_group
25
+ if self.redis
26
+ self.redis.keys(self.redis_prefix + self.user.uuid.to_s + '_*').each do |data|
27
+ self.redis.del data
28
+ end
29
+ self.redis.del self.redis_prefix + 'user:' + self.user.uuid.to_s
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ class Anoubis::Sso::Server::System < Anoubis::Core::ApplicationRecord
2
+ self.table_name = 'systems'
3
+
4
+ before_validation :before_validation_anoubis_sso_server_system_on_create, on: :create
5
+ after_save :after_save_anoubis_sso_server_system
6
+ after_destroy :after_destroy_anoubis_sso_server_system
7
+
8
+ #VALID_HTTP_REGEX = /\A(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?\Z/i
9
+
10
+ validates :title, presence: true, length: { maximum: 100 }
11
+ validates :host, presence: true, length: { maximum: 200 }#, format: { with: VALID_HTTP_REGEX }
12
+ validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
13
+
14
+ enum status: { enabled: 0, disabled: 1 }
15
+
16
+ def before_validation_anoubis_sso_server_system_on_create
17
+ self.uuid = SecureRandom.uuid unless self.uuid
18
+ self.secret_key = SecureRandom.uuid unless self.secret_key
19
+ end
20
+
21
+ def after_save_anoubis_sso_server_system
22
+ if self.status == 'enabled'
23
+ self.redis.set self.redis_cache_name, { host: self.host, secret_key: self.secret_key, callback: self.callback, silent: self.silent }.to_json
24
+ else
25
+ self.after_destroy_system
26
+ end
27
+ end
28
+
29
+ def after_destroy_anubis_sso_server_system
30
+ self.redis.del self.redis_cache_name
31
+ end
32
+
33
+ def redis_cache_name
34
+ self.redis_prefix + 'system:' + self.uuid
35
+ end
36
+ end
@@ -0,0 +1,79 @@
1
+ class Anubis::Sso::Server::User < Anubis::Core::ApplicationRecord
2
+ has_secure_password
3
+
4
+ self.table_name = 'users'
5
+
6
+ before_validation :before_validation_anubis_sso_server_user_on_create, on: :create
7
+ before_save :before_save_anubis_sso_server_user
8
+ after_destroy :after_destroy_anubis_sso_server_user
9
+
10
+ VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
11
+
12
+ validates :login, presence: true, length: { maximum: 50 },
13
+ format: { with: VALID_EMAIL_REGEX },
14
+ uniqueness: { case_sensitive: true }
15
+
16
+ validates :name, presence: true, length: { maximum: 100 }
17
+ validates :surname, presence: true, length: { maximum: 100 }
18
+
19
+ validates :password, length: { in: 5..30 }, on: [:create]
20
+ validates :password, length: { in: 5..30 }, on: [:update], if: :password_changed?
21
+ validates :password_confirmation, length: { in: 5..30 }, on: [:create]
22
+ validates :password_confirmation, length: { in: 5..30 }, on: [:update], if: :password_changed?
23
+
24
+ validates :auth_key, length: { maximum: 32 }
25
+
26
+ validates :timeout, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 300 }
27
+
28
+ validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
29
+
30
+ enum status: { pending: 0, enabled: 1, disabled: 100 }
31
+ validates :status, inclusion: { in: self.statuses }
32
+
33
+ enum role: { user_role: 0, admin_role: 100 }
34
+ validates :role, inclusion: { in: self.roles }
35
+
36
+ def before_validation_anubis_sso_server_user_on_create
37
+ self.uuid = SecureRandom.uuid
38
+ self.timezone = 'GMT' if !self.timezone
39
+ end
40
+
41
+ def before_save_anubis_sso_server_user
42
+ self.timezone = 'GMT' if !self.timezone
43
+ self.login = self.login.downcase
44
+ self.clear_cache
45
+ end
46
+
47
+ def after_destroy_anubis_sso_server_user
48
+ self.clear_cache
49
+ end
50
+
51
+ def save_cache
52
+ self.redis.set(self.redis_prefix + 'user:' + self.uuid, self.to_json(except: [:password_digest])) if self.redis
53
+ end
54
+
55
+ def clear_cache
56
+ self.redis.del(self.redis_prefix + 'user:' + self.uuid) if self.redis
57
+ end
58
+
59
+ def password_changed?
60
+ !password.blank?
61
+ end
62
+
63
+ def self.load_cache(redis, uuid)
64
+ begin
65
+ data = JSON.parse redis.get(User.redis_prefix + 'user:' + uuid), { symbolize_names: true }
66
+ rescue
67
+ data = nil
68
+ end
69
+
70
+ unless data
71
+ user = self.where(uuid: uuid).first
72
+ if user
73
+ return JSON.parse(user.to_json(except: [:password_digest]), { symbolize_names: true })
74
+ end
75
+ end
76
+
77
+ data
78
+ end
79
+ end
@@ -0,0 +1,41 @@
1
+ ##
2
+ # Default ApplicationRecord for Anoubis::Core library.
3
+ class Anoubis::Tenant::ApplicationRecord < Anoubis::Core::ApplicationRecord
4
+ self.abstract_class = true
5
+
6
+ before_update :before_update_tenant_anoubis_model
7
+ before_create :before_create_tenant_anoubis_model
8
+
9
+ ##
10
+ # Returns the default ActiveRecord 'where' for defined model.
11
+ # @param object [ApplicationController] pointer to used Application controller
12
+ # @param pid [Integer] parent model id if present (default: 0). Variable doesn't necessary
13
+ # @return [Hash] ActiveRecord 'where' definition
14
+ def self.get_where(object, pid = 0)
15
+ if self.has_attribute? :tenant_id
16
+ return { tenant_id: object.current_user.tenant_id }
17
+ else
18
+ return {}
19
+ end
20
+ end
21
+
22
+ ##
23
+ # Is called before data will be updated in database. Prevents changing tenant
24
+ def before_update_tenant_anoubis_model
25
+ begin
26
+ self.tenant_id = self.tenant_id_was
27
+ rescue
28
+
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Is called before data will be created in database. Sets tenant according by current user
34
+ def before_create_tenant_anoubis_model
35
+ begin
36
+ self.tenant_id = self.current_user.tenant_id
37
+ rescue
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,95 @@
1
+ ##
2
+ # Group model. Stores information about usser's groups
3
+ class Anoubis::Tenant::Group < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'groups'
6
+
7
+ before_validation :before_validation_group_create, on: :create
8
+ before_validation :before_validation_group_update, on: :update
9
+ before_save :before_save_group
10
+ before_destroy :before_destroy_group
11
+
12
+ # Group's identifier consists of lowercase alphabetic symbols.
13
+ VALID_IDENT_REGEX = /[a-z]\z/i
14
+
15
+ # @!attribute ident
16
+ # @return [String] the group's identifier. Identifier consists of lowercase alphabetical symbols.
17
+ validates :ident, length: { minimum: 3, maximum: 50 }, uniqueness: { scope: [:system_id], case_sensitive: false }, format: { with: VALID_IDENT_REGEX }
18
+
19
+ # @!attribute full_ident
20
+ # @return [String] the calculated group's identification. This identification based on {System#ident} and {#ident}
21
+
22
+ # @!attribute system
23
+ # @return [System] reference to system that owns this group
24
+ belongs_to :system, class_name: 'Anoubis::Tenant::System'
25
+
26
+ has_many :group_menus, class_name: 'Anoubis::Tenant::GroupMenu'
27
+ has_many :user_groups, class_name: 'Anoubis::Tenant::UserGroup'
28
+ has_many :group_locales, class_name: 'Anoubis::Tenant::GroupLocale'
29
+
30
+ ##
31
+ # Is called before validation when new group is being created. Sets {#ident} value as 'admin' and {#system}
32
+ # value to main system with id 1 if this is a first group.
33
+ def before_validation_group_create
34
+ if self.id
35
+ if self.id == 1
36
+ self.ident = 'admin'
37
+ self.system_id = 1
38
+ return true
39
+ end
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Is called before validation when group is being updated. Prevents changing {#ident} value when {#ident}
45
+ # value is 'admin'
46
+ def before_validation_group_update
47
+ if self.ident != self.ident_was && self.ident_was == 'admin'
48
+ errors.add(:ident, I18n.t('tims.groups.errors.cant_change_admin_ident'))
49
+ throw(:abort, __method__)
50
+ end
51
+ end
52
+
53
+ ##
54
+ # Is called before group will be stored in database. Changes {#ident} value to lower case.
55
+ def before_save_group
56
+ self.full_ident = self.system.ident+'.'+self.ident
57
+ end
58
+
59
+ ##
60
+ # Is called before group will be deleted from database. Checks the ability to destroy a group. Prevents deleting
61
+ # group with id 1. Also destroys all associated links to {User} and {Menu}
62
+ def before_destroy_group
63
+ # Can't destroy admin group of main system
64
+ if self.ident == 'admin' && self.system_id == 1
65
+ errors.add(:base, I18n.t('anubis.groups.errors.cant_destroy_admin_group'))
66
+ throw(:abort, __method__)
67
+ end
68
+
69
+ Anoubis::Tenant::GroupLocale.where(group_id: self.id).each do |group_locale|
70
+ group_locale.destroy
71
+ end
72
+
73
+ unless user_groups.empty?
74
+ errors.add(:base, I18n.t('anubis.groups.errors.cant_destroy_group_with_users'))
75
+ throw(:abort, __method__)
76
+ end
77
+
78
+ # Before destroy group delete all associated links to users and menus
79
+ Anoubis::Tenant::GroupMenu.where(group_id: self.id).delete_all
80
+ #Anoubis::Tenant::UserGroup.where(group_id: self.id).delete_all
81
+ end
82
+
83
+ ##
84
+ # Returns model localization data from {GroupLocale}.
85
+ # @return [GroupLocale] localization for current group
86
+ def model_locale
87
+ @model_locale ||= self.group_locales.where(locale: Anoubis::Tenant::GroupLocale.locales[self.current_locale.to_sym]).first
88
+ end
89
+
90
+ # @!attribute title
91
+ # @return [String] the group's title. Title loads from {GroupLocale#title} based on {#current_locale}
92
+ def title
93
+ self.model_locale.title if self.model_locale
94
+ end
95
+ end
@@ -0,0 +1,19 @@
1
+ ##
2
+ # Localization for {Group} model. Model stores all translations for {Group} model.
3
+ class Anoubis::Tenant::GroupLocale < Anoubis::Core::ApplicationRecord
4
+ # Redefines default table name
5
+ self.table_name = 'group_locales'
6
+
7
+ # @!attribute title
8
+ # @return [String] the group's localized title
9
+ validates :title, length: { minimum: 3, maximum: 100 }
10
+
11
+ # @!attribute menu
12
+ # @return [Group] reference to the {Group} model
13
+ belongs_to :group, :class_name => 'Anoubis::Tenant::Group'
14
+ validates :group, presence: true, uniqueness: { scope: [:locale] }
15
+
16
+ # @!attribute locale
17
+ # @return [Locales] reference to locale
18
+ enum locale: Anoubis::Core::Locales.enums
19
+ end