ditty 0.7.1 → 0.10.1

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/.env.test +2 -0
  3. data/.gitignore +3 -0
  4. data/.pryrc +2 -0
  5. data/.rubocop.yml +24 -8
  6. data/.travis.yml +4 -8
  7. data/CNAME +1 -0
  8. data/Dockerfile +18 -0
  9. data/Gemfile.ci +0 -15
  10. data/Rakefile +5 -4
  11. data/Readme.md +24 -2
  12. data/_config.yml +1 -0
  13. data/config.ru +4 -4
  14. data/ditty.gemspec +31 -20
  15. data/docs/CNAME +1 -0
  16. data/docs/_config.yml +1 -0
  17. data/docs/index.md +34 -0
  18. data/exe/ditty +2 -0
  19. data/lib/ditty.rb +30 -4
  20. data/lib/ditty/cli.rb +38 -5
  21. data/lib/ditty/components/ditty.rb +82 -0
  22. data/lib/ditty/controllers/application_controller.rb +267 -0
  23. data/lib/ditty/controllers/{audit_logs.rb → audit_logs_controller.rb} +5 -7
  24. data/lib/ditty/controllers/{auth.rb → auth_controller.rb} +56 -32
  25. data/lib/ditty/controllers/{component.rb → component_controller.rb} +35 -24
  26. data/lib/ditty/controllers/{main.rb → main_controller.rb} +7 -7
  27. data/lib/ditty/controllers/roles_controller.rb +23 -0
  28. data/lib/ditty/controllers/user_login_traits_controller.rb +46 -0
  29. data/lib/ditty/controllers/{users.rb → users_controller.rb} +17 -20
  30. data/lib/ditty/db.rb +9 -5
  31. data/lib/ditty/emails/base.rb +48 -34
  32. data/lib/ditty/generators/crud_generator.rb +114 -0
  33. data/lib/ditty/generators/migration_generator.rb +26 -0
  34. data/lib/ditty/generators/project_generator.rb +52 -0
  35. data/lib/ditty/helpers/authentication.rb +6 -5
  36. data/lib/ditty/helpers/component.rb +11 -2
  37. data/lib/ditty/helpers/pundit.rb +24 -8
  38. data/lib/ditty/helpers/response.rb +38 -15
  39. data/lib/ditty/helpers/views.rb +48 -6
  40. data/lib/ditty/listener.rb +44 -14
  41. data/lib/ditty/memcached.rb +8 -0
  42. data/lib/ditty/middleware/accept_extension.rb +4 -2
  43. data/lib/ditty/middleware/error_catchall.rb +4 -2
  44. data/lib/ditty/models/audit_log.rb +1 -0
  45. data/lib/ditty/models/base.rb +13 -0
  46. data/lib/ditty/models/identity.rb +10 -7
  47. data/lib/ditty/models/role.rb +2 -0
  48. data/lib/ditty/models/user.rb +40 -3
  49. data/lib/ditty/models/user_login_trait.rb +17 -0
  50. data/lib/ditty/policies/audit_log_policy.rb +6 -6
  51. data/lib/ditty/policies/role_policy.rb +3 -3
  52. data/lib/ditty/policies/user_login_trait_policy.rb +45 -0
  53. data/lib/ditty/policies/user_policy.rb +3 -3
  54. data/lib/ditty/rubocop.rb +3 -0
  55. data/lib/ditty/seed.rb +2 -0
  56. data/lib/ditty/services/authentication.rb +31 -15
  57. data/lib/ditty/services/email.rb +22 -12
  58. data/lib/ditty/services/logger.rb +30 -13
  59. data/lib/ditty/services/pagination_wrapper.rb +9 -5
  60. data/lib/ditty/services/settings.rb +19 -7
  61. data/lib/ditty/tasks/ditty.rake +127 -0
  62. data/lib/ditty/tasks/omniauth-ldap.rake +43 -0
  63. data/lib/ditty/templates/.gitignore +5 -0
  64. data/lib/ditty/templates/.rspec +2 -0
  65. data/lib/ditty/templates/.rubocop.yml +7 -0
  66. data/lib/ditty/templates/Rakefile +12 -0
  67. data/lib/ditty/templates/application.rb +12 -0
  68. data/lib/ditty/templates/config.ru +37 -0
  69. data/lib/ditty/templates/controller.rb.erb +64 -0
  70. data/lib/ditty/templates/env.example +4 -0
  71. data/lib/ditty/templates/lib/project.rb.erb +5 -0
  72. data/lib/ditty/templates/migration.rb.erb +7 -0
  73. data/lib/ditty/templates/model.rb.erb +26 -0
  74. data/lib/ditty/templates/pids/.empty_directory +0 -0
  75. data/lib/ditty/templates/policy.rb.erb +48 -0
  76. data/{public → lib/ditty/templates/public}/browserconfig.xml +0 -0
  77. data/lib/ditty/templates/public/css/sb-admin-2.min.css +10 -0
  78. data/lib/ditty/templates/public/css/styles.css +13 -0
  79. data/lib/ditty/templates/public/favicon.ico +0 -0
  80. data/{public → lib/ditty/templates/public}/images/apple-icon.png +0 -0
  81. data/{public → lib/ditty/templates/public}/images/favicon-16x16.png +0 -0
  82. data/{public → lib/ditty/templates/public}/images/favicon-32x32.png +0 -0
  83. data/{public → lib/ditty/templates/public}/images/launcher-icon-1x.png +0 -0
  84. data/{public → lib/ditty/templates/public}/images/launcher-icon-2x.png +0 -0
  85. data/{public → lib/ditty/templates/public}/images/launcher-icon-4x.png +0 -0
  86. data/{public → lib/ditty/templates/public}/images/mstile-150x150.png +0 -0
  87. data/{public → lib/ditty/templates/public}/images/safari-pinned-tab.svg +0 -0
  88. data/lib/ditty/templates/public/js/sb-admin-2.min.js +7 -0
  89. data/lib/ditty/templates/public/js/scripts.js +1 -0
  90. data/{public/manifest.json → lib/ditty/templates/public/manifest.json.erb} +2 -2
  91. data/lib/ditty/templates/settings.yml.erb +19 -0
  92. data/lib/ditty/templates/sidekiq.rb +18 -0
  93. data/lib/ditty/templates/sidekiq.yml +9 -0
  94. data/lib/ditty/templates/spec_helper.rb +43 -0
  95. data/lib/ditty/templates/type.rb.erb +21 -0
  96. data/lib/ditty/templates/views/display.haml.tt +20 -0
  97. data/lib/ditty/templates/views/edit.haml.tt +10 -0
  98. data/lib/ditty/templates/views/form.haml.tt +11 -0
  99. data/lib/ditty/templates/views/index.haml.tt +29 -0
  100. data/lib/ditty/templates/views/new.haml.tt +10 -0
  101. data/lib/ditty/version.rb +1 -1
  102. data/lib/rubocop/cop/ditty/call_services_directly.rb +42 -0
  103. data/migrate/20181209_add_user_login_traits.rb +16 -0
  104. data/migrate/20181209_extend_audit_log.rb +12 -0
  105. data/migrate/20190220_add_parent_id_to_roles.rb +9 -0
  106. data/spec/ditty/api_spec.rb +51 -0
  107. data/spec/ditty/controllers/roles_spec.rb +67 -0
  108. data/spec/ditty/controllers/user_login_traits_spec.rb +72 -0
  109. data/spec/ditty/controllers/users_spec.rb +72 -0
  110. data/spec/ditty/emails/base_spec.rb +76 -0
  111. data/spec/ditty/emails/forgot_password_spec.rb +20 -0
  112. data/spec/ditty/helpers/component_spec.rb +85 -0
  113. data/spec/ditty/models/user_spec.rb +36 -0
  114. data/spec/ditty/services/email_spec.rb +36 -0
  115. data/spec/ditty/services/logger_spec.rb +68 -0
  116. data/spec/ditty/services/settings_spec.rb +63 -0
  117. data/spec/ditty_spec.rb +9 -0
  118. data/spec/factories.rb +46 -0
  119. data/spec/fixtures/logger.yml +17 -0
  120. data/spec/fixtures/section.yml +3 -0
  121. data/spec/fixtures/settings.yml +8 -0
  122. data/spec/spec_helper.rb +51 -0
  123. data/spec/support/api_shared_examples.rb +250 -0
  124. data/spec/support/crud_shared_examples.rb +145 -0
  125. data/views/403.haml +2 -0
  126. data/views/404.haml +2 -4
  127. data/views/500.haml +11 -0
  128. data/views/audit_logs/index.haml +32 -28
  129. data/views/auth/forgot_password.haml +32 -16
  130. data/views/auth/identity.haml +14 -13
  131. data/views/auth/ldap.haml +17 -0
  132. data/views/auth/login.haml +23 -17
  133. data/views/auth/register.haml +20 -18
  134. data/views/auth/register_identity.haml +27 -12
  135. data/views/auth/reset_password.haml +36 -19
  136. data/views/blank.haml +43 -0
  137. data/views/emails/forgot_password.haml +1 -1
  138. data/views/emails/layouts/action.haml +10 -6
  139. data/views/emails/layouts/alert.haml +2 -1
  140. data/views/emails/layouts/billing.haml +2 -1
  141. data/views/embedded.haml +17 -11
  142. data/views/error.haml +8 -3
  143. data/views/index.haml +1 -1
  144. data/views/layout.haml +45 -30
  145. data/views/partials/actions.haml +15 -14
  146. data/views/partials/content_tag.haml +0 -0
  147. data/views/partials/delete_form.haml +1 -1
  148. data/views/partials/filter_control.haml +2 -2
  149. data/views/partials/footer.haml +13 -5
  150. data/views/partials/form_control.haml +30 -19
  151. data/views/partials/form_tag.haml +1 -1
  152. data/views/partials/navitems.haml +42 -0
  153. data/views/partials/notifications.haml +12 -8
  154. data/views/partials/pager.haml +44 -25
  155. data/views/partials/search.haml +15 -11
  156. data/views/partials/sidebar.haml +15 -37
  157. data/views/partials/sort_ui.haml +2 -0
  158. data/views/partials/timespan_selector.haml +64 -0
  159. data/views/partials/topbar.haml +53 -0
  160. data/views/partials/user_associations.haml +32 -0
  161. data/views/quick_start.haml +23 -0
  162. data/views/roles/display.haml +27 -6
  163. data/views/roles/edit.haml +3 -3
  164. data/views/roles/form.haml +1 -0
  165. data/views/roles/index.haml +23 -14
  166. data/views/roles/new.haml +2 -2
  167. data/views/user_login_traits/display.haml +32 -0
  168. data/views/user_login_traits/edit.haml +10 -0
  169. data/views/user_login_traits/form.haml +5 -0
  170. data/views/user_login_traits/index.haml +28 -0
  171. data/views/user_login_traits/new.haml +10 -0
  172. data/views/users/display.haml +15 -16
  173. data/views/users/edit.haml +3 -3
  174. data/views/users/form.haml +0 -0
  175. data/views/users/index.haml +31 -24
  176. data/views/users/login_traits.haml +25 -0
  177. data/views/users/new.haml +2 -2
  178. data/views/users/profile.haml +17 -15
  179. data/views/users/user.haml +1 -1
  180. metadata +314 -76
  181. data/lib/ditty/components/app.rb +0 -77
  182. data/lib/ditty/controllers/application.rb +0 -175
  183. data/lib/ditty/controllers/roles.rb +0 -16
  184. data/lib/ditty/rake_tasks.rb +0 -102
  185. data/views/partials/navbar.haml +0 -23
@@ -1,53 +1,63 @@
1
- require 'ditty/controllers/application'
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/controllers/application_controller'
2
4
  require 'ditty/services/email'
3
5
  require 'securerandom'
4
6
 
5
7
  module Ditty
6
- class Auth < Application
8
+ class AuthController < ApplicationController
7
9
  set track_actions: true
8
10
 
9
- def find_template(views, name, engine, &block)
10
- super(views, name, engine, &block) # Root
11
- super(::Ditty::App.view_folder, name, engine, &block) # Basic Plugin
11
+ def redirect_path
12
+ return "#{settings.map_path}/" if omniauth_redirect_path.nil?
13
+ return "#{settings.map_path}/" if omniauth_redirect_path.match? %r{/#{settings.map_path}/auth/?}
14
+
15
+ omniauth_redirect_path
12
16
  end
13
17
 
14
- def redirect_path
15
- return "#{settings.map_path}/" unless env['omniauth.origin']
16
- return "#{settings.map_path}/" if env['omniauth.origin'] =~ %r{/#{settings.map_path}/auth/?}
17
- env['omniauth.origin']
18
+ def omniauth_redirect_path
19
+ env['omniauth.origin'] || request.session['omniauth.origin']
18
20
  end
19
21
 
20
22
  def omniauth_callback(provider)
21
23
  return failed_login unless env['omniauth.auth']
24
+
25
+ broadcast("before_#{provider}_login".to_sym, env['omniauth.auth'])
22
26
  user = User.first(email: env['omniauth.auth']['info']['email'])
23
- user = register_user if user.nil? && ['ldap', 'google_oauth2'].include?(provider)
27
+ user = register_user if user.nil? && authorize(::Ditty::User, :register?)
24
28
  return failed_login if user.nil?
29
+
30
+ broadcast("#{provider}_login".to_sym, user)
25
31
  successful_login(user)
26
32
  end
27
33
 
28
34
  def failed_login
29
- broadcast(:user_failed_login, target: self, details: "IP: #{request.ip}")
30
- flash[:warning] = 'Invalid credentials. Please try again.'
35
+ details = params[:message] || 'None'
36
+ logger.warn "Invalid Login: #{details}"
37
+ broadcast(:user_failed_login, target: self, details: details)
38
+ flash[:warning] = 'Invalid credentials. Please try again'
39
+ headers 'X-Authentication-Failure' => params[:message] if params[:message]
31
40
  redirect "#{settings.map_path}/auth/login"
32
41
  end
33
42
 
34
43
  def successful_login(user)
35
44
  halt 200 if request.xhr?
36
45
  self.current_user = user
37
- broadcast(:user_login, target: self, details: "IP: #{request.ip}")
46
+ broadcast(:user_login, target: self)
38
47
  flash[:success] = 'Logged In'
39
48
  redirect redirect_path
40
49
  end
41
50
 
42
51
  def register_user
43
52
  user = User.create(email: env['omniauth.auth']['info']['email'])
44
- broadcast(:user_register, target: self, values: { user: user }, details: "IP: #{request.ip}")
53
+ broadcast(:user_register, target: self, values: { user: user })
45
54
  flash[:info] = 'Successfully Registered.'
46
55
  user
47
56
  end
48
57
 
49
58
  before '/login' do
50
59
  return if User.where(roles: Role.find_or_create(name: 'super_admin')).count.positive?
60
+
51
61
  flash[:info] = 'Please register the super admin user.'
52
62
  redirect "#{settings.map_path}/auth/register"
53
63
  end
@@ -55,14 +65,24 @@ module Ditty
55
65
  # TODO: Make this work for both LDAP and Identity
56
66
  get '/login' do
57
67
  authorize ::Ditty::Identity, :login
68
+ redirect settings.map_path if authenticated?
58
69
 
59
- haml :'auth/login', locals: { title: 'Log In' }
70
+ haml :'auth/login', locals: { title: 'Log In' }, layout: :blank
71
+ end
72
+
73
+ # Custom login form for LDAP to allow CSRF checks. Set the `request_path` for
74
+ # the omniauth-ldap provider to another path so that this gest triggered
75
+ get '/ldap' do
76
+ authorize ::Ditty::Identity, :login
77
+ redirect settings.map_path if authenticated?
78
+
79
+ haml :'auth/ldap', locals: { title: 'Company Log In' }, layout: :blank
60
80
  end
61
81
 
62
82
  get '/forgot-password' do
63
83
  authorize ::Ditty::Identity, :forgot_password
64
84
 
65
- haml :'auth/forgot_password', locals: { title: 'Forgot your password?' }
85
+ haml :'auth/forgot_password', locals: { title: 'Forgot your password?' }, layout: :blank
66
86
  end
67
87
 
68
88
  post '/forgot-password' do
@@ -76,15 +96,18 @@ module Ditty
76
96
  token = SecureRandom.hex(16)
77
97
  identity.update(reset_token: token, reset_requested: Time.now)
78
98
  # Send Email
79
- reset_url = "#{request.base_url}#{settings.map_path}/reset-password?token=#{token}"
80
- Ditty::Services::Email.deliver(
99
+ reset_url = "#{request.base_url}#{settings.map_path}/auth/reset-password?token=#{token}"
100
+ ::Ditty::Services::Email.deliver(
81
101
  :forgot_password,
82
102
  email,
83
103
  locals: { identity: identity, reset_url: reset_url, request: request }
84
104
  )
85
105
  end
86
106
  flash[:info] = 'An email was sent to the email provided with instructions on how to reset your password'
87
- redirect '/login'
107
+ redirect "#{settings.map_path}/auth/login"
108
+ rescue Sinatra::Param::InvalidParameterError
109
+ flash[:warning] = 'Email address not provided'
110
+ redirect back
88
111
  end
89
112
 
90
113
  get '/reset-password' do
@@ -92,9 +115,9 @@ module Ditty
92
115
 
93
116
  param :token, String, required: true
94
117
  identity = Identity[reset_token: params[:token]]
95
- halt 404 unless identity && identity.reset_requested && identity.reset_requested > (Time.now - (24 * 60 * 60))
118
+ halt 404 unless identity&.reset_requested && identity.reset_requested > (Time.now - (24 * 60 * 60))
96
119
 
97
- haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
120
+ haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }, layout: :blank
98
121
  end
99
122
 
100
123
  put '/reset-password' do
@@ -104,15 +127,15 @@ module Ditty
104
127
  halt 404 unless identity
105
128
  authorize identity, :reset_password
106
129
 
107
- identity_params = permitted_attributes(Identity, :update)
130
+ identity_params = permitted_parameters(Identity, :update)
108
131
  identity.set identity_params.merge(reset_token: nil, reset_requested: nil)
109
- if identity.valid? && identity.save
110
- broadcast(:identity_update_password, target: self, details: "IP: #{request.ip}")
132
+ if identity.valid? && identity.save_changes
133
+ broadcast(:identity_update_password, target: self)
111
134
  flash[:success] = 'Password Updated'
112
135
  redirect "#{settings.map_path}/auth/login"
113
136
  else
114
- broadcast(:identity_update_password_failed, target: self, details: "IP: #{request.ip}")
115
- haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
137
+ broadcast(:identity_update_password_failed, target: self)
138
+ haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }, layout: :blank
116
139
  end
117
140
  end
118
141
 
@@ -121,7 +144,7 @@ module Ditty
121
144
  authorize ::Ditty::User.new, :register
122
145
 
123
146
  identity = Identity.new
124
- haml :'auth/register', locals: { title: 'Register', identity: identity }
147
+ haml :'auth/register', locals: { title: 'Register', identity: identity }, layout: :blank
125
148
  end
126
149
 
127
150
  # Register Action
@@ -132,27 +155,28 @@ module Ditty
132
155
  authorize user, :register
133
156
 
134
157
  begin
158
+ identity.valid?
135
159
  DB.transaction do
136
- user.save
160
+ user.save_changes
137
161
  user.add_identity identity
138
- broadcast(:user_register, target: self, values: { user: user }, details: "IP: #{request.ip}")
162
+ broadcast(:user_register, target: self, values: { user: user })
139
163
  flash[:info] = 'Successfully Registered. Please log in'
140
164
  redirect "#{settings.map_path}/auth/login"
141
165
  end
142
166
  rescue Sequel::ValidationFailed
143
167
  flash.now[:warning] = 'Could not complete the registration. Please try again.'
144
- haml :'auth/register', locals: { identity: identity }
168
+ haml :'auth/register', locals: { identity: identity }, layout: :blank
145
169
  end
146
170
  end
147
171
 
148
172
  # Logout Action
149
173
  delete '/' do
150
- broadcast(:user_logout, target: self, details: "IP: #{request.ip}")
174
+ broadcast(:user_logout, target: self)
151
175
  logout
152
176
 
153
177
  halt 200 if request.xhr?
154
178
  flash[:info] = 'Logged Out'
155
- redirect(Ditty::Services::Settings[:logout_redirect_path] || "#{settings.map_path}/")
179
+ redirect(::Ditty::Services::Settings[:logout_redirect_path] || "#{settings.map_path}/")
156
180
  end
157
181
 
158
182
  # Unauthenticated
@@ -1,36 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/application'
3
+ require 'ditty/controllers/application_controller'
4
4
  require 'ditty/helpers/component'
5
5
  require 'ditty/helpers/response'
6
6
  require 'sinatra/json'
7
7
 
8
8
  module Ditty
9
- class Component < Application
9
+ class ComponentController < ApplicationController
10
10
  helpers Helpers::Component, Helpers::Response
11
11
 
12
12
  set base_path: nil
13
13
  set dehumanized: nil
14
14
  set view_location: nil
15
15
  set track_actions: false
16
+ set heading: nil
16
17
 
17
18
  def read(id)
18
- dataset.first(settings.model_class.primary_key => id)
19
+ dataset.with_pk(id)
20
+ end
21
+
22
+ def read!(id)
23
+ halt 404 unless (entity = read(id))
24
+ entity
19
25
  end
20
26
 
21
27
  def skip_verify!
22
28
  @skip_verify = true
23
29
  end
24
30
 
31
+ def trigger(event, attribs = {})
32
+ attribs[:target] ||= self
33
+ send(event, attribs) if respond_to? event
34
+ broadcast(event, attribs)
35
+ end
36
+
25
37
  after do
26
38
  return if settings.environment == 'production'
27
- if (response.successful? || response.redirection?) && @skip_verify == false
28
- verify_authorized if settings.environment != 'production'
39
+
40
+ if (response.successful? || response.redirection?) && @skip_verify == false && (settings.environment != 'production')
41
+ verify_authorized
29
42
  end
30
43
  end
31
44
 
32
45
  after '/' do
33
46
  return if settings.environment == 'production' || request.request_method != 'GET'
47
+
34
48
  verify_policy_scoped if (response.successful? || response.redirection?) && @skip_verify == false
35
49
  end
36
50
 
@@ -40,7 +54,8 @@ module Ditty
40
54
 
41
55
  result = list
42
56
 
43
- broadcast(:component_list, target: self)
57
+ trigger :component_list
58
+
44
59
  list_response(result)
45
60
  end
46
61
 
@@ -48,8 +63,7 @@ module Ditty
48
63
  get '/new/?' do
49
64
  authorize settings.model_class, :create
50
65
 
51
- entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
52
- session[:redirect_to] = request.fullpath
66
+ entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
53
67
  haml :"#{view_location}/new",
54
68
  locals: { entity: entity, title: heading(:new) },
55
69
  layout: layout
@@ -57,12 +71,12 @@ module Ditty
57
71
 
58
72
  # Create
59
73
  post '/' do
60
- entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
74
+ entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
61
75
  authorize entity, :create
62
76
 
63
77
  entity.db.transaction do
64
- entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
65
- broadcast(:component_create, target: self, entity: entity)
78
+ entity.save_changes # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
79
+ trigger :component_create, entity: entity
66
80
  end
67
81
 
68
82
  create_response(entity)
@@ -70,20 +84,19 @@ module Ditty
70
84
 
71
85
  # Read
72
86
  get '/:id/?' do |id|
73
- entity = read(id)
74
- halt 404 unless entity
87
+ entity = read!(id)
75
88
  authorize entity, :read
76
89
 
77
- broadcast(:component_read, target: self, entity: entity)
90
+ trigger :component_read, entity: entity
78
91
  read_response(entity)
79
92
  end
80
93
 
81
94
  # Update Form
82
95
  get '/:id/edit/?' do |id|
83
- entity = read(id)
84
- halt 404 unless entity
96
+ entity = read!(id)
85
97
  authorize entity, :update
86
98
 
99
+ flash[:redirect_to] = "#{base_path}/#{entity.display_id}" unless flash.keep(:redirect_to)
87
100
  haml :"#{view_location}/edit",
88
101
  locals: { entity: entity, title: heading(:edit) },
89
102
  layout: layout
@@ -91,27 +104,25 @@ module Ditty
91
104
 
92
105
  # Update
93
106
  put '/:id/?' do |id|
94
- entity = read(id)
95
- halt 404 unless entity
107
+ entity = read!(id)
96
108
  authorize entity, :update
97
109
 
98
110
  entity.db.transaction do
99
- entity.set(permitted_attributes(settings.model_class, :update))
100
- entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
101
- broadcast(:component_update, target: self, entity: entity)
111
+ entity.set(permitted_parameters(settings.model_class, :update))
112
+ entity.save_changes # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
113
+ trigger :component_update, entity: entity
102
114
  end
103
115
 
104
116
  update_response(entity)
105
117
  end
106
118
 
107
119
  delete '/:id/?' do |id|
108
- entity = read(id)
109
- halt 404 unless entity
120
+ entity = read!(id)
110
121
  authorize entity, :delete
111
122
 
112
123
  entity.db.transaction do
113
124
  entity.destroy
114
- broadcast(:component_delete, target: self, entity: entity)
125
+ trigger :component_delete, entity: entity
115
126
  end
116
127
 
117
128
  delete_response(entity)
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/application'
3
+ require 'ditty/controllers/application_controller'
4
4
  require 'ditty/models/role'
5
5
  require 'ditty/models/user'
6
+ require 'ditty/services/settings'
6
7
 
7
8
  module Ditty
8
- class Main < Application
9
+ class MainController < ApplicationController
9
10
  set track_actions: true
10
11
 
11
- def find_template(views, name, engine, &block)
12
- super(views, name, engine, &block) # Root
13
- super(::Ditty::App.view_folder, name, engine, &block) # Basic Plugin
14
- end
15
-
16
12
  before '/' do
17
13
  return if User.where(roles: Role.find_or_create(name: 'super_admin')).count.positive?
14
+
18
15
  flash[:info] = 'Please register the super admin user.'
19
16
  redirect "#{settings.map_path}/auth/register"
20
17
  end
21
18
 
22
19
  # Home Page
23
20
  get '/' do
21
+ home_page = Services::Settings['ditty.home_page']
22
+ redirect "#{settings.map_path}#{home_page}" if home_page
23
+
24
24
  authenticate!
25
25
  haml :index, locals: { title: 'Home' }
26
26
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/controllers/component_controller'
4
+ require 'ditty/models/role'
5
+ require 'ditty/policies/role_policy'
6
+
7
+ module Ditty
8
+ class RolesController < ::Ditty::ComponentController
9
+ SEARCHABLE = %i[name].freeze
10
+
11
+ set model_class: Role
12
+
13
+ helpers do
14
+ def parent_options(entity)
15
+ return policy_scope(::Ditty::Role) if entity.new?
16
+
17
+ policy_scope(::Ditty::Role)
18
+ .exclude(id: [entity.id] + entity.descendants.map(&:id))
19
+ .order(:name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/controllers/component_controller'
4
+ require 'ditty/models/user_login_trait'
5
+ require 'ditty/policies/user_login_trait_policy'
6
+
7
+ module Ditty
8
+ class UserLoginTraitsController < ::Ditty::ComponentController
9
+ SEARCHABLE = %i[platform device browser ip_address].freeze
10
+ FILTERS = [
11
+ { name: :user, field: 'user.email' },
12
+ { name: :platform },
13
+ { name: :device },
14
+ { name: :browser }
15
+ ].freeze
16
+
17
+ set base_path: '/login-traits'
18
+ set model_class: UserLoginTrait
19
+ set heading: 'Login'
20
+ # set track_actions: true
21
+
22
+ helpers do
23
+ def user_options
24
+ policy_scope(::Ditty::User).as_hash(:email, :email)
25
+ end
26
+
27
+ def platform_options
28
+ policy_scope(::Ditty::UserLoginTrait).select(:platform).distinct.as_hash(:platform, :platform)
29
+ end
30
+
31
+ def device_options
32
+ policy_scope(::Ditty::UserLoginTrait).select(:device).distinct.as_hash(:device, :device)
33
+ end
34
+
35
+ def browser_options
36
+ policy_scope(::Ditty::UserLoginTrait).select(:browser).distinct.as_hash(:browser, :browser)
37
+ end
38
+ end
39
+
40
+ def list
41
+ return super if params[:sort]
42
+
43
+ super.order(:updated_at).reverse
44
+ end
45
+ end
46
+ end