ditty 0.7.2 → 0.8.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -7
  3. data/.travis.yml +5 -5
  4. data/Gemfile.ci +2 -0
  5. data/Rakefile +4 -3
  6. data/Readme.md +24 -2
  7. data/ditty.gemspec +4 -3
  8. data/lib/ditty.rb +24 -0
  9. data/lib/ditty/cli.rb +6 -2
  10. data/lib/ditty/components/app.rb +10 -1
  11. data/lib/ditty/controllers/application.rb +72 -10
  12. data/lib/ditty/controllers/audit_logs.rb +1 -5
  13. data/lib/ditty/controllers/auth.rb +37 -17
  14. data/lib/ditty/controllers/component.rb +15 -5
  15. data/lib/ditty/controllers/main.rb +1 -5
  16. data/lib/ditty/controllers/roles.rb +2 -5
  17. data/lib/ditty/controllers/user_login_traits.rb +18 -0
  18. data/lib/ditty/controllers/users.rb +4 -9
  19. data/lib/ditty/db.rb +3 -1
  20. data/lib/ditty/emails/base.rb +13 -4
  21. data/lib/ditty/helpers/authentication.rb +6 -5
  22. data/lib/ditty/helpers/component.rb +9 -1
  23. data/lib/ditty/helpers/response.rb +24 -3
  24. data/lib/ditty/helpers/views.rb +20 -0
  25. data/lib/ditty/listener.rb +38 -10
  26. data/lib/ditty/middleware/accept_extension.rb +2 -0
  27. data/lib/ditty/middleware/error_catchall.rb +2 -0
  28. data/lib/ditty/models/audit_log.rb +1 -0
  29. data/lib/ditty/models/base.rb +4 -0
  30. data/lib/ditty/models/identity.rb +3 -0
  31. data/lib/ditty/models/role.rb +1 -0
  32. data/lib/ditty/models/user.rb +9 -1
  33. data/lib/ditty/models/user_login_trait.rb +17 -0
  34. data/lib/ditty/policies/audit_log_policy.rb +6 -6
  35. data/lib/ditty/policies/role_policy.rb +2 -2
  36. data/lib/ditty/policies/user_login_trait_policy.rb +45 -0
  37. data/lib/ditty/policies/user_policy.rb +2 -2
  38. data/lib/ditty/rubocop.rb +3 -0
  39. data/lib/ditty/seed.rb +2 -0
  40. data/lib/ditty/services/authentication.rb +7 -2
  41. data/lib/ditty/services/email.rb +8 -2
  42. data/lib/ditty/services/logger.rb +11 -0
  43. data/lib/ditty/services/pagination_wrapper.rb +2 -0
  44. data/lib/ditty/services/settings.rb +14 -3
  45. data/lib/ditty/tasks/ditty.rake +109 -0
  46. data/lib/ditty/tasks/omniauth-ldap.rake +43 -0
  47. data/lib/ditty/version.rb +1 -1
  48. data/lib/rubocop/cop/ditty/call_services_directly.rb +42 -0
  49. data/migrate/20181209_add_user_login_traits.rb +16 -0
  50. data/migrate/20181209_extend_audit_log.rb +12 -0
  51. data/views/403.haml +2 -0
  52. data/views/audit_logs/index.haml +11 -6
  53. data/views/auth/ldap.haml +17 -0
  54. data/views/emails/forgot_password.haml +1 -1
  55. data/views/emails/layouts/action.haml +10 -6
  56. data/views/emails/layouts/alert.haml +2 -1
  57. data/views/emails/layouts/billing.haml +2 -1
  58. data/views/error.haml +8 -3
  59. data/views/partials/form_control.haml +24 -20
  60. data/views/partials/navbar.haml +11 -12
  61. data/views/partials/sidebar.haml +1 -1
  62. data/views/roles/index.haml +2 -0
  63. data/views/user_login_traits/display.haml +32 -0
  64. data/views/user_login_traits/edit.haml +10 -0
  65. data/views/user_login_traits/form.haml +5 -0
  66. data/views/user_login_traits/index.haml +30 -0
  67. data/views/user_login_traits/new.haml +10 -0
  68. data/views/users/display.haml +1 -1
  69. data/views/users/login_traits.haml +27 -0
  70. data/views/users/profile.haml +2 -0
  71. metadata +50 -21
  72. data/lib/ditty/rake_tasks.rb +0 -102
@@ -3,7 +3,8 @@
3
3
  %head
4
4
  %meta{:content => "width=device-width", :name => "viewport"}/
5
5
  %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
6
- %title Alerts e.g. approaching your limit
6
+ %title
7
+ = subject
7
8
  :css
8
9
  img {
9
10
  max-width: 100%;
@@ -3,7 +3,8 @@
3
3
  %head
4
4
  %meta{:content => "width=device-width", :name => "viewport"}/
5
5
  %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
6
- %title Billing e.g. invoices and receipts
6
+ %title
7
+ = subject
7
8
  :css
8
9
  img {
9
10
  max-width: 100%;
@@ -1,4 +1,9 @@
1
- %h2 Whoops!
1
+ - if ENV['APP_ENV'] != 'production'
2
+ %p.lead
3
+ = error.class
4
+ %br
5
+ %small
6
+ = error.message
2
7
 
3
- %p.lead
4
- = error.class
8
+ %pre
9
+ = error.backtrace.join("\n")
@@ -1,20 +1,24 @@
1
- %div{ class: "form-group#{model.errors[field] ? ' has-error' : ''}" }
2
- %label.col-sm-3.control-label{ for: attributes[:id] }= label
3
- .col-sm-9
4
- - type = attributes.delete(:type)
5
- - if type == 'select'
6
- - options = attributes.delete(:options)
7
- %select{attributes}
8
- - if attributes[:multiple]
9
- - options.each do |k,v| k ||= v; v ||= k;
10
- %option{ value: k, selected: (model.send(field).map(&:id).include? k)}= v
11
- - else
12
- %option{ value: ""} -- Select One --
13
- - options.each do |k,v| k ||= v; v ||= k;
14
- %option{ value: k, selected: [model[field].to_s, default].include?(k.to_s)}= v
15
- - elsif type == 'textarea'
16
- %textarea{attributes}= preserve(model[field])
17
- - else
18
- %input{attributes, type: type, value: model[field] || default}
19
- - if model.errors[field]
20
- %p.help-block.text-danger= model.errors[field].join(', ')
1
+ - type = attributes.delete(:type)
2
+ - if type == 'hidden'
3
+ %input{attributes, type: type, value: model[field] || default}
4
+ - else
5
+ %div{ class: "form-group#{model.errors[field] ? ' has-error' : ''}" }
6
+ - if type != 'file'
7
+ %label.col-sm-3.control-label{ for: attributes[:id] }= label
8
+ .col-sm-9
9
+ - if type == 'select'
10
+ - options = attributes.delete(:options)
11
+ %select{attributes}
12
+ - if attributes[:multiple]
13
+ - options.each do |k,v| k ||= v; v ||= k;
14
+ %option{ value: k, selected: (model.send(field).map(&:id).include? k)}= v
15
+ - else
16
+ %option{ value: ""} -- Select One --
17
+ - options.each do |k,v| k ||= v; v ||= k;
18
+ %option{ value: k, selected: [model[field].to_s, default].include?(k.to_s)}= v
19
+ - elsif type == 'textarea'
20
+ %textarea{attributes}= preserve(model[field] || default)
21
+ - else
22
+ %input{attributes, type: type, value: model[field] || default}
23
+ - if model.errors[field]
24
+ %p.help-block.text-danger= model.errors[field].join(', ')
@@ -6,18 +6,17 @@
6
6
  %span.icon-bar
7
7
  %span.icon-bar
8
8
  %span.navbar-brand
9
- Ditty
9
+ = config('ditty.title', 'Ditty')
10
+
11
+ %nav.nav.navbar-top-links.navbar-right.navbar-form{ style: 'border: 0;' }
12
+ - if authenticated?
13
+ = delete_form_tag("#{settings.map_path}/auth") do
14
+ %a.btn.btn-default{ href: "#{settings.map_path}/users/profile" } My Account
15
+ %button.btn.btn-default{ type: 'submit' }
16
+ Logout
17
+ - else
18
+ %a.btn.btn-default{ href: "#{settings.map_path}/auth/login" }
19
+ Log In
10
20
 
11
- -if authenticated?
12
- = delete_form_tag("#{settings.map_path}/auth", attributes: { class: 'nav navbar-top-links navbar-form navbar-right' }) do
13
- %a.btn.btn-default{ href: "#{settings.map_path}/users/profile" } My Account
14
- %button.btn.btn-default{ type: 'submit' }
15
- / %i.ti-panel
16
- Logout
17
- - else
18
- %ul.nav.navbar-top-links.navbar-right
19
- %li
20
- %a.btn.btn-link{ href: "#{settings.map_path}/auth/login" }
21
- Log In
22
21
  .navbar-default.sidebar{ role: 'navigation' }
23
22
  = haml :'partials/sidebar'
@@ -26,7 +26,7 @@
26
26
  %i.fa.fa-fw{ class: "fa-#{item[:icon]}" }
27
27
  = item[:text]
28
28
  - else
29
- %li.active
29
+ %li
30
30
  %a{ href: "#{settings.map_path}/auth/login" }
31
31
  %i.fa.fa-user.fa-fw
32
32
  Log In
@@ -1,6 +1,8 @@
1
1
  .row
2
2
  .col-md-12
3
3
  .panel.panel-default
4
+ .panel-body
5
+ = haml :'partials/search'
4
6
  %table.table.table-striped
5
7
  %thead
6
8
  %tr
@@ -0,0 +1,32 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ %p.description
7
+ %label User:
8
+ = entity.user&.email || 'Unknown'
9
+ %p.description
10
+ %label IP Address:
11
+ = entity.ip_address || 'Unknown'
12
+ %p.description
13
+ %label Device:
14
+ = entity.device || 'Unknown'
15
+ %p.description
16
+ %label Platform:
17
+ = entity.platform || 'Unknown'
18
+ %p.description
19
+ %label Browser:
20
+ = entity.browser || 'Unknown'
21
+ %p.description
22
+ %label Last Seen:
23
+ = entity.updated_at.strftime('%F %T')
24
+
25
+ .row
26
+ .col-md-6
27
+ %a.btn.btn-default{ href: "#{base_path}/#{entity.id}/edit" } Edit
28
+ .col-md-6.text-right
29
+ - if policy(entity).delete?
30
+ = delete_form_tag("#{base_path}/#{entity.id}") do
31
+ %button.btn.btn-warning{ type: 'submit' } Delete
32
+ .col-md-2
@@ -0,0 +1,10 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ = edit_form_tag "#{base_path}/#{entity.id}" do
7
+ = haml :'user_login_traits/form', locals: { entity: entity }
8
+ %button.btn.btn-primary{ type: 'submit' }
9
+ Update Login Trait
10
+ .col-md-2
@@ -0,0 +1,5 @@
1
+ = form_control(:ip_address, entity)
2
+ = form_control(:device, entity)
3
+ = form_control(:platform, entity)
4
+ = form_control(:browser, entity)
5
+ = form_control(:updated_at, entity)
@@ -0,0 +1,30 @@
1
+ .row
2
+ .col-md-12
3
+ .panel.panel-default
4
+ .panel-body
5
+ = haml :'partials/search'
6
+ %table.table.table-striped
7
+ %thead
8
+ %tr
9
+ %th User
10
+ %th IP Address
11
+ %th Device
12
+ %th Platform
13
+ %th Browser
14
+ %th Last Seen
15
+ %tbody
16
+ - if list.count.positive?
17
+ - list.all.each do |entity|
18
+ %tr
19
+ %td= entity.user&.email || 'Unknown'
20
+ %td
21
+ %a{ href: "#{base_path}/#{entity.id}" }= entity.ip_address || 'Unknown'
22
+ %td= entity.device || 'Unknown'
23
+ %td= entity.platform || 'Unknown'
24
+ %td= entity.browser || 'Unknown'
25
+ %td= entity.updated_at
26
+ - else
27
+ %tr
28
+ %td.text-center{ colspan: 6 } No records
29
+
30
+ =pagination(list, base_path)
@@ -0,0 +1,10 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-body
6
+ = new_form_tag base_path do
7
+ = haml :'user_login_traits/form', locals: { entity: entity }
8
+ %button.btn.btn-primary{ type: 'submit' }
9
+ Create Login Trait
10
+ .col-md-2
@@ -46,4 +46,4 @@
46
46
  Change Password
47
47
  .col-md-2
48
48
 
49
-
49
+ = haml :'users/login_traits', locals: { user_login_traits: entity.user_login_traits }
@@ -0,0 +1,27 @@
1
+ .row
2
+ .col-md-2
3
+ .col-md-8
4
+ .panel.panel-default
5
+ .panel-heading
6
+ %h4 Logins
7
+ %table.table
8
+ %thead
9
+ %tr
10
+ %th IP Address
11
+ %th Device
12
+ %th Platform
13
+ %th Browser
14
+ %th Last Seen
15
+ %tbody
16
+ - if user_login_traits.count.positive?
17
+ - user_login_traits.each do |trait|
18
+ %tr
19
+ %td= trait.ip_address
20
+ %td= trait.device
21
+ %td= trait.platform
22
+ %td= trait.browser
23
+ %td= trait.updated_at.strftime('%F %T')
24
+ - else
25
+ %tr
26
+ %td.text-center{ colspan: 5 } No Records
27
+ .col-md-2
@@ -44,3 +44,5 @@
44
44
  %button.btn.btn-primary{ type: 'submit' }
45
45
  Change Password
46
46
  .col-md-2
47
+
48
+ = haml :'users/login_traits', locals: { user_login_traits: entity.user_login_traits }
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ditty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgens du Toit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-03 00:00:00.000000000 Z
11
+ date: 2019-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.12'
26
+ version: '1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: database_cleaner
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '3.1'
139
+ - !ruby/object:Gem::Dependency
140
+ name: browser
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.5'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.5'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: haml
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -165,33 +179,33 @@ dependencies:
165
179
  - !ruby/object:Gem::Version
166
180
  version: '1.0'
167
181
  - !ruby/object:Gem::Dependency
168
- name: oga
182
+ name: mail
169
183
  requirement: !ruby/object:Gem::Requirement
170
184
  requirements:
171
185
  - - ">="
172
186
  - !ruby/object:Gem::Version
173
- version: '2.14'
187
+ version: '1.7'
174
188
  type: :runtime
175
189
  prerelease: false
176
190
  version_requirements: !ruby/object:Gem::Requirement
177
191
  requirements:
178
192
  - - ">="
179
193
  - !ruby/object:Gem::Version
180
- version: '2.14'
194
+ version: '1.7'
181
195
  - !ruby/object:Gem::Dependency
182
- name: mail
196
+ name: oga
183
197
  requirement: !ruby/object:Gem::Requirement
184
198
  requirements:
185
199
  - - ">="
186
200
  - !ruby/object:Gem::Version
187
- version: '1.7'
201
+ version: '2.14'
188
202
  type: :runtime
189
203
  prerelease: false
190
204
  version_requirements: !ruby/object:Gem::Requirement
191
205
  requirements:
192
206
  - - ">="
193
207
  - !ruby/object:Gem::Version
194
- version: '1.7'
208
+ version: '2.14'
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: omniauth
197
211
  requirement: !ruby/object:Gem::Requirement
@@ -347,33 +361,33 @@ dependencies:
347
361
  - !ruby/object:Gem::Version
348
362
  version: '1.5'
349
363
  - !ruby/object:Gem::Dependency
350
- name: tilt
364
+ name: thor
351
365
  requirement: !ruby/object:Gem::Requirement
352
366
  requirements:
353
367
  - - ">="
354
368
  - !ruby/object:Gem::Version
355
- version: '2'
369
+ version: '0.20'
356
370
  type: :runtime
357
371
  prerelease: false
358
372
  version_requirements: !ruby/object:Gem::Requirement
359
373
  requirements:
360
374
  - - ">="
361
375
  - !ruby/object:Gem::Version
362
- version: '2'
376
+ version: '0.20'
363
377
  - !ruby/object:Gem::Dependency
364
- name: thor
378
+ name: tilt
365
379
  requirement: !ruby/object:Gem::Requirement
366
380
  requirements:
367
381
  - - ">="
368
382
  - !ruby/object:Gem::Version
369
- version: '0.20'
383
+ version: '2'
370
384
  type: :runtime
371
385
  prerelease: false
372
386
  version_requirements: !ruby/object:Gem::Requirement
373
387
  requirements:
374
388
  - - ">="
375
389
  - !ruby/object:Gem::Version
376
- version: '0.20'
390
+ version: '2'
377
391
  - !ruby/object:Gem::Dependency
378
392
  name: will_paginate
379
393
  requirement: !ruby/object:Gem::Requirement
@@ -432,6 +446,7 @@ files:
432
446
  - lib/ditty/controllers/component.rb
433
447
  - lib/ditty/controllers/main.rb
434
448
  - lib/ditty/controllers/roles.rb
449
+ - lib/ditty/controllers/user_login_traits.rb
435
450
  - lib/ditty/controllers/users.rb
436
451
  - lib/ditty/db.rb
437
452
  - lib/ditty/emails/base.rb
@@ -449,23 +464,30 @@ files:
449
464
  - lib/ditty/models/identity.rb
450
465
  - lib/ditty/models/role.rb
451
466
  - lib/ditty/models/user.rb
467
+ - lib/ditty/models/user_login_trait.rb
452
468
  - lib/ditty/policies/application_policy.rb
453
469
  - lib/ditty/policies/audit_log_policy.rb
454
470
  - lib/ditty/policies/identity_policy.rb
455
471
  - lib/ditty/policies/role_policy.rb
472
+ - lib/ditty/policies/user_login_trait_policy.rb
456
473
  - lib/ditty/policies/user_policy.rb
457
- - lib/ditty/rake_tasks.rb
474
+ - lib/ditty/rubocop.rb
458
475
  - lib/ditty/seed.rb
459
476
  - lib/ditty/services/authentication.rb
460
477
  - lib/ditty/services/email.rb
461
478
  - lib/ditty/services/logger.rb
462
479
  - lib/ditty/services/pagination_wrapper.rb
463
480
  - lib/ditty/services/settings.rb
481
+ - lib/ditty/tasks/ditty.rake
482
+ - lib/ditty/tasks/omniauth-ldap.rake
464
483
  - lib/ditty/version.rb
484
+ - lib/rubocop/cop/ditty/call_services_directly.rb
465
485
  - migrate/20170207_base_tables.rb
466
486
  - migrate/20170208_audit_log.rb
467
487
  - migrate/20170416_audit_log_details.rb
468
488
  - migrate/20180307_password_reset.rb
489
+ - migrate/20181209_add_user_login_traits.rb
490
+ - migrate/20181209_extend_audit_log.rb
469
491
  - public/browserconfig.xml
470
492
  - public/images/apple-icon.png
471
493
  - public/images/favicon-16x16.png
@@ -477,10 +499,12 @@ files:
477
499
  - public/images/safari-pinned-tab.svg
478
500
  - public/manifest.json
479
501
  - views/400.haml
502
+ - views/403.haml
480
503
  - views/404.haml
481
504
  - views/audit_logs/index.haml
482
505
  - views/auth/forgot_password.haml
483
506
  - views/auth/identity.haml
507
+ - views/auth/ldap.haml
484
508
  - views/auth/login.haml
485
509
  - views/auth/register.haml
486
510
  - views/auth/register_identity.haml
@@ -510,10 +534,16 @@ files:
510
534
  - views/roles/form.haml
511
535
  - views/roles/index.haml
512
536
  - views/roles/new.haml
537
+ - views/user_login_traits/display.haml
538
+ - views/user_login_traits/edit.haml
539
+ - views/user_login_traits/form.haml
540
+ - views/user_login_traits/index.haml
541
+ - views/user_login_traits/new.haml
513
542
  - views/users/display.haml
514
543
  - views/users/edit.haml
515
544
  - views/users/identity.haml
516
545
  - views/users/index.haml
546
+ - views/users/login_traits.haml
517
547
  - views/users/new.haml
518
548
  - views/users/profile.haml
519
549
  - views/users/user.haml
@@ -536,8 +566,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
536
566
  - !ruby/object:Gem::Version
537
567
  version: '0'
538
568
  requirements: []
539
- rubyforge_project:
540
- rubygems_version: 2.7.7
569
+ rubygems_version: 3.0.2
541
570
  signing_key:
542
571
  specification_version: 4
543
572
  summary: Sinatra Based Application Framework